/** * @file tests/unit/test_encode_device.cpp * @brief Tests for typed AVCodec hardware device initialization. */ // test includes #include "../tests_common.h" // standard includes #include #include // local includes #include #include namespace video { util::Either vaapi_init_avcodec_hardware_input_buffer(platf::avcodec_encode_device_t *encode_device); #ifdef SUNSHINE_BUILD_VULKAN util::Either vulkan_init_avcodec_hardware_input_buffer(platf::avcodec_encode_device_t *encode_device); #endif } // namespace video namespace { /** * @brief Fake backend that records initialization and supplies an FFmpeg buffer. */ struct tracked_encode_device_t: platf::avcodec_encode_device_t { int calls = 0; ///< Number of backend initialization calls. int status = 0; ///< Status returned by the fake backend. AVBufferRef *created_buffer = nullptr; ///< Non-owning identity of the buffer returned to FFmpeg. tracked_encode_device_t() { hardware = true; init_hw_device = &initialize; } /** * @brief Initialize a tracked hardware buffer or return the configured error. * @param device Fake encode device whose invocation is recorded. * @param buffer Receives the hardware buffer on success. * @return Configured failure status or zero on success. */ static int initialize(platf::avcodec_encode_device_t *device, AVBufferRef **buffer) { auto *tracked = static_cast(device); ++tracked->calls; if (tracked->status) { return tracked->status; } tracked->created_buffer = av_buffer_alloc(1); *buffer = tracked->created_buffer; return *buffer ? 0 : AVERROR(ENOMEM); } }; /** * @brief Supported hardware initialization entry points under test. */ using initializer_t = util::Either (*)(platf::avcodec_encode_device_t *); /** * @brief Hardware initialization entry points available in this build. */ constexpr std::array hardware_initializers { &video::vaapi_init_avcodec_hardware_input_buffer, #ifdef SUNSHINE_BUILD_VULKAN &video::vulkan_init_avcodec_hardware_input_buffer, #endif }; /** * @brief Exercise the same typed backend contract for each hardware entry point. */ struct EncodeDeviceInitializationTest: testing::TestWithParam {}; } // namespace static_assert(std::is_same_v); static_assert(std::is_same_v); TEST(EncodeDeviceStateTest, SoftwareDeviceDoesNotAdvertiseHardwareResources) { platf::avcodec_encode_device_t device; EXPECT_FALSE(device.hardware); EXPECT_EQ(device.init_hw_device, nullptr); #ifdef _WIN32 EXPECT_EQ(device.d3d11_device, nullptr); #endif } TEST_P(EncodeDeviceInitializationTest, UsesBackendBufferWithoutOpeningAnotherDevice) { tracked_encode_device_t device; auto result = GetParam()(&device); ASSERT_TRUE(result.has_left()); ASSERT_NE(result.left().get(), nullptr); EXPECT_EQ(result.left().get(), device.created_buffer); EXPECT_EQ(device.calls, 1); } TEST_P(EncodeDeviceInitializationTest, PropagatesBackendFailureWithoutUsingFallback) { tracked_encode_device_t device; device.status = AVERROR(EINVAL); auto result = GetParam()(&device); ASSERT_FALSE(result.has_left()); EXPECT_EQ(result.right(), -1); EXPECT_EQ(device.created_buffer, nullptr); EXPECT_EQ(device.calls, 1); } INSTANTIATE_TEST_SUITE_P( HardwareBackends, EncodeDeviceInitializationTest, testing::ValuesIn(hardware_initializers) );