feat(tests): add unit tests for render coordinator initialization order

This commit is contained in:
2026-01-08 00:33:17 +00:00
parent de8ad12a7b
commit 0417fbf5ba
3 changed files with 139 additions and 0 deletions

View File

@@ -552,6 +552,18 @@ target_link_libraries(bgfx_frame_requirement_test PRIVATE
SDL3::SDL3
)
add_test(NAME bgfx_frame_requirement_test COMMAND bgfx_frame_requirement_test)
# Test: Render coordinator initialization order (TDD guard for shader load timing)
add_executable(render_coordinator_init_order_test
tests/render_coordinator_init_order_test.cpp
src/services/impl/render_coordinator_service.cpp
)
target_include_directories(render_coordinator_init_order_test PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_link_libraries(render_coordinator_init_order_test PRIVATE
GTest::gtest
GTest::gtest_main
)
add_test(NAME render_coordinator_init_order_test COMMAND render_coordinator_init_order_test)
endif()
if(ENABLE_VITA)

View File

@@ -48,6 +48,24 @@ void RenderCoordinatorService::RenderFrame(float time) {
}
return;
}
if (logger_) {
logger_->Trace("RenderCoordinatorService", "RenderFrame",
"Priming bgfx with a dummy frame before shader load");
}
if (!graphicsService_->BeginFrame()) {
if (logger_) {
logger_->Warn("RenderCoordinatorService::RenderFrame: Swapchain out of date during shader pre-frame");
}
graphicsService_->RecreateSwapchain();
return;
}
if (!graphicsService_->EndFrame()) {
if (logger_) {
logger_->Warn("RenderCoordinatorService::RenderFrame: Swapchain out of date during shader pre-frame");
}
graphicsService_->RecreateSwapchain();
return;
}
if (logger_) {
logger_->Trace("RenderCoordinatorService", "RenderFrame", "Loading shaders from Lua");
}

View File

@@ -0,0 +1,109 @@
#include <gtest/gtest.h>
#include "services/impl/render_coordinator_service.hpp"
#include "services/interfaces/i_graphics_service.hpp"
#include "services/interfaces/i_shader_script_service.hpp"
#include <algorithm>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
namespace {
class CallOrderGraphicsService : public sdl3cpp::services::IGraphicsService {
public:
std::vector<std::string> calls;
bool beginFrameResult = true;
bool endFrameResult = true;
void InitializeDevice(SDL_Window*, const sdl3cpp::services::GraphicsConfig&) override {}
void InitializeSwapchain() override {}
void RecreateSwapchain() override { calls.push_back("RecreateSwapchain"); }
void Shutdown() noexcept override {}
void LoadShaders(const std::unordered_map<std::string, sdl3cpp::services::ShaderPaths>&) override {
calls.push_back("LoadShaders");
}
void UploadVertexData(const std::vector<sdl3cpp::core::Vertex>&) override {}
void UploadIndexData(const std::vector<uint16_t>&) override {}
bool BeginFrame() override {
calls.push_back("BeginFrame");
return beginFrameResult;
}
void RenderScene(const std::vector<sdl3cpp::services::RenderCommand>&,
const sdl3cpp::services::ViewState&) override {}
bool EndFrame() override {
calls.push_back("EndFrame");
return endFrameResult;
}
void WaitIdle() override {}
sdl3cpp::services::GraphicsDeviceHandle GetDevice() const override { return nullptr; }
sdl3cpp::services::GraphicsDeviceHandle GetPhysicalDevice() const override { return nullptr; }
std::pair<uint32_t, uint32_t> GetSwapchainExtent() const override { return {0, 0}; }
uint32_t GetSwapchainFormat() const override { return 0; }
void* GetCurrentCommandBuffer() const override { return nullptr; }
void* GetGraphicsQueue() const override { return nullptr; }
};
class StubShaderScriptService : public sdl3cpp::services::IShaderScriptService {
public:
std::unordered_map<std::string, sdl3cpp::services::ShaderPaths> LoadShaderPathsMap() override {
return {};
}
};
std::string JoinCalls(const std::vector<std::string>& calls) {
std::string joined;
for (size_t index = 0; index < calls.size(); ++index) {
if (index > 0) {
joined += " -> ";
}
joined += calls[index];
}
return joined;
}
bool HasEndFrameBeforeLoadShaders(const std::vector<std::string>& calls) {
for (const auto& call : calls) {
if (call == "LoadShaders") {
return false;
}
if (call == "EndFrame") {
return true;
}
}
return false;
}
TEST(RenderCoordinatorInitOrderTest, LoadsShadersOnlyAfterFirstFrame) {
auto graphicsService = std::make_shared<CallOrderGraphicsService>();
auto shaderScriptService = std::make_shared<StubShaderScriptService>();
sdl3cpp::services::impl::RenderCoordinatorService service(
nullptr,
graphicsService,
nullptr,
shaderScriptService,
nullptr,
nullptr,
nullptr);
service.RenderFrame(0.0f);
const bool hasLoadShaders = std::find(
graphicsService->calls.begin(),
graphicsService->calls.end(),
"LoadShaders") != graphicsService->calls.end();
if (!hasLoadShaders) {
SUCCEED() << "RenderFrame did not load shaders; initialization is expected elsewhere.";
return;
}
EXPECT_TRUE(HasEndFrameBeforeLoadShaders(graphicsService->calls))
<< "LoadShaders was called before a completed frame. Calls: "
<< JoinCalls(graphicsService->calls);
}
} // namespace