Add XCUI expansion state and coverage tests
This commit is contained in:
283
tests/NewEditor/test_imgui_window_ui_compositor.cpp
Normal file
283
tests/NewEditor/test_imgui_window_ui_compositor.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "XCUIBackend/IEditorHostCompositor.h"
|
||||
#include "XCUIBackend/ImGuiWindowUICompositor.h"
|
||||
#include "XCUIBackend/UITextureRegistration.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::Editor::Platform::D3D12WindowRenderer;
|
||||
using XCEngine::Editor::XCUIBackend::IEditorHostCompositor;
|
||||
using XCEngine::Editor::XCUIBackend::ImGuiWindowUICompositor;
|
||||
using XCEngine::Editor::XCUIBackend::UITextureRegistration;
|
||||
|
||||
class RecordingHostCompositor final : public IEditorHostCompositor {
|
||||
public:
|
||||
bool initializeResult = true;
|
||||
bool handleWindowMessageResult = false;
|
||||
bool createTextureDescriptorResult = false;
|
||||
bool invokeConfigureFonts = false;
|
||||
|
||||
int initializeCount = 0;
|
||||
int shutdownCount = 0;
|
||||
int beginFrameCount = 0;
|
||||
int endFrameCount = 0;
|
||||
int handleWindowMessageCount = 0;
|
||||
int createTextureDescriptorCount = 0;
|
||||
int freeTextureDescriptorCount = 0;
|
||||
|
||||
HWND lastHwnd = nullptr;
|
||||
UINT lastMessage = 0u;
|
||||
WPARAM lastWParam = 0u;
|
||||
LPARAM lastLParam = 0u;
|
||||
|
||||
D3D12WindowRenderer* initializedRenderer = nullptr;
|
||||
D3D12WindowRenderer* presentedRenderer = nullptr;
|
||||
::XCEngine::RHI::RHIDevice* lastDevice = nullptr;
|
||||
::XCEngine::RHI::RHITexture* lastTexture = nullptr;
|
||||
|
||||
bool beforeUiRenderProvided = false;
|
||||
bool afterUiRenderProvided = false;
|
||||
|
||||
std::array<float, 4> lastClearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
UITextureRegistration nextRegistration = {};
|
||||
UITextureRegistration freedRegistration = {};
|
||||
std::vector<std::string> callOrder = {};
|
||||
|
||||
bool Initialize(
|
||||
HWND hwnd,
|
||||
D3D12WindowRenderer& windowRenderer,
|
||||
const ConfigureFontsCallback& configureFonts) override {
|
||||
++initializeCount;
|
||||
lastHwnd = hwnd;
|
||||
initializedRenderer = &windowRenderer;
|
||||
callOrder.push_back("initialize");
|
||||
if (invokeConfigureFonts && configureFonts) {
|
||||
configureFonts();
|
||||
}
|
||||
return initializeResult;
|
||||
}
|
||||
|
||||
void Shutdown() override {
|
||||
++shutdownCount;
|
||||
callOrder.push_back("shutdown");
|
||||
}
|
||||
|
||||
bool HandleWindowMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) override {
|
||||
++handleWindowMessageCount;
|
||||
lastHwnd = hwnd;
|
||||
lastMessage = message;
|
||||
lastWParam = wParam;
|
||||
lastLParam = lParam;
|
||||
callOrder.push_back("message");
|
||||
return handleWindowMessageResult;
|
||||
}
|
||||
|
||||
void BeginFrame() override {
|
||||
++beginFrameCount;
|
||||
callOrder.push_back("begin");
|
||||
}
|
||||
|
||||
void EndFrameAndPresent(
|
||||
D3D12WindowRenderer& windowRenderer,
|
||||
const float clearColor[4],
|
||||
const RenderCallback& beforeUiRender,
|
||||
const RenderCallback& afterUiRender) override {
|
||||
++endFrameCount;
|
||||
presentedRenderer = &windowRenderer;
|
||||
beforeUiRenderProvided = static_cast<bool>(beforeUiRender);
|
||||
afterUiRenderProvided = static_cast<bool>(afterUiRender);
|
||||
for (std::size_t index = 0; index < lastClearColor.size(); ++index) {
|
||||
lastClearColor[index] = clearColor[index];
|
||||
}
|
||||
callOrder.push_back("present");
|
||||
}
|
||||
|
||||
bool CreateTextureDescriptor(
|
||||
::XCEngine::RHI::RHIDevice* device,
|
||||
::XCEngine::RHI::RHITexture* texture,
|
||||
UITextureRegistration& outRegistration) override {
|
||||
++createTextureDescriptorCount;
|
||||
lastDevice = device;
|
||||
lastTexture = texture;
|
||||
if (createTextureDescriptorResult) {
|
||||
outRegistration = nextRegistration;
|
||||
}
|
||||
return createTextureDescriptorResult;
|
||||
}
|
||||
|
||||
void FreeTextureDescriptor(const UITextureRegistration& registration) override {
|
||||
++freeTextureDescriptorCount;
|
||||
freedRegistration = registration;
|
||||
}
|
||||
};
|
||||
|
||||
HWND MakeFakeHwnd() {
|
||||
return reinterpret_cast<HWND>(static_cast<std::uintptr_t>(0x1234u));
|
||||
}
|
||||
|
||||
UITextureRegistration MakeRegistration() {
|
||||
UITextureRegistration registration = {};
|
||||
registration.cpuHandle.ptr = 11u;
|
||||
registration.gpuHandle.ptr = 29u;
|
||||
registration.texture.nativeHandle = 43u;
|
||||
registration.texture.width = 256u;
|
||||
registration.texture.height = 128u;
|
||||
registration.texture.kind = ::XCEngine::UI::UITextureHandleKind::ShaderResourceView;
|
||||
return registration;
|
||||
}
|
||||
|
||||
TEST(ImGuiWindowUICompositorTest, InitializeForwardsToHostAndConfigureFontsCallback) {
|
||||
auto host = std::make_unique<RecordingHostCompositor>();
|
||||
RecordingHostCompositor* hostPtr = host.get();
|
||||
hostPtr->invokeConfigureFonts = true;
|
||||
|
||||
ImGuiWindowUICompositor compositor(std::move(host));
|
||||
D3D12WindowRenderer renderer = {};
|
||||
bool configureFontsCalled = false;
|
||||
|
||||
EXPECT_TRUE(compositor.Initialize(
|
||||
MakeFakeHwnd(),
|
||||
renderer,
|
||||
[&configureFontsCalled]() { configureFontsCalled = true; }));
|
||||
EXPECT_TRUE(configureFontsCalled);
|
||||
ASSERT_NE(hostPtr, nullptr);
|
||||
EXPECT_EQ(hostPtr->initializeCount, 1);
|
||||
EXPECT_EQ(hostPtr->lastHwnd, MakeFakeHwnd());
|
||||
EXPECT_EQ(hostPtr->initializedRenderer, &renderer);
|
||||
}
|
||||
|
||||
TEST(ImGuiWindowUICompositorTest, RenderFrameCallsHostBeginUiAndPresentInOrder) {
|
||||
auto host = std::make_unique<RecordingHostCompositor>();
|
||||
RecordingHostCompositor* hostPtr = host.get();
|
||||
|
||||
ImGuiWindowUICompositor compositor(std::move(host));
|
||||
D3D12WindowRenderer renderer = {};
|
||||
ASSERT_TRUE(compositor.Initialize(MakeFakeHwnd(), renderer, {}));
|
||||
hostPtr->callOrder.clear();
|
||||
|
||||
bool uiRendered = false;
|
||||
constexpr float clearColor[4] = { 0.1f, 0.2f, 0.3f, 0.4f };
|
||||
compositor.RenderFrame(
|
||||
clearColor,
|
||||
[&]() {
|
||||
uiRendered = true;
|
||||
hostPtr->callOrder.push_back("ui");
|
||||
},
|
||||
[](const ::XCEngine::Rendering::RenderContext&, const ::XCEngine::Rendering::RenderSurface&) {},
|
||||
[](const ::XCEngine::Rendering::RenderContext&, const ::XCEngine::Rendering::RenderSurface&) {});
|
||||
|
||||
EXPECT_TRUE(uiRendered);
|
||||
EXPECT_EQ(hostPtr->beginFrameCount, 1);
|
||||
EXPECT_EQ(hostPtr->endFrameCount, 1);
|
||||
EXPECT_EQ(hostPtr->presentedRenderer, &renderer);
|
||||
EXPECT_TRUE(hostPtr->beforeUiRenderProvided);
|
||||
EXPECT_TRUE(hostPtr->afterUiRenderProvided);
|
||||
EXPECT_EQ(hostPtr->lastClearColor[0], clearColor[0]);
|
||||
EXPECT_EQ(hostPtr->lastClearColor[1], clearColor[1]);
|
||||
EXPECT_EQ(hostPtr->lastClearColor[2], clearColor[2]);
|
||||
EXPECT_EQ(hostPtr->lastClearColor[3], clearColor[3]);
|
||||
EXPECT_EQ(
|
||||
hostPtr->callOrder,
|
||||
(std::vector<std::string>{ "begin", "ui", "present" }));
|
||||
}
|
||||
|
||||
TEST(ImGuiWindowUICompositorTest, HandleWindowMessageAndTextureRegistrationForwardToHost) {
|
||||
auto host = std::make_unique<RecordingHostCompositor>();
|
||||
RecordingHostCompositor* hostPtr = host.get();
|
||||
hostPtr->handleWindowMessageResult = true;
|
||||
hostPtr->createTextureDescriptorResult = true;
|
||||
hostPtr->nextRegistration = MakeRegistration();
|
||||
|
||||
ImGuiWindowUICompositor compositor(std::move(host));
|
||||
|
||||
EXPECT_TRUE(compositor.HandleWindowMessage(MakeFakeHwnd(), WM_SIZE, 7u, 19u));
|
||||
EXPECT_EQ(hostPtr->handleWindowMessageCount, 1);
|
||||
EXPECT_EQ(hostPtr->lastMessage, static_cast<UINT>(WM_SIZE));
|
||||
EXPECT_EQ(hostPtr->lastWParam, static_cast<WPARAM>(7u));
|
||||
EXPECT_EQ(hostPtr->lastLParam, static_cast<LPARAM>(19u));
|
||||
|
||||
UITextureRegistration registration = {};
|
||||
auto* fakeDevice = reinterpret_cast<::XCEngine::RHI::RHIDevice*>(static_cast<std::uintptr_t>(0x41u));
|
||||
auto* fakeTexture = reinterpret_cast<::XCEngine::RHI::RHITexture*>(static_cast<std::uintptr_t>(0x59u));
|
||||
EXPECT_TRUE(compositor.CreateTextureDescriptor(fakeDevice, fakeTexture, registration));
|
||||
EXPECT_EQ(hostPtr->createTextureDescriptorCount, 1);
|
||||
EXPECT_EQ(hostPtr->lastDevice, fakeDevice);
|
||||
EXPECT_EQ(hostPtr->lastTexture, fakeTexture);
|
||||
EXPECT_EQ(registration.cpuHandle.ptr, hostPtr->nextRegistration.cpuHandle.ptr);
|
||||
EXPECT_EQ(registration.gpuHandle.ptr, hostPtr->nextRegistration.gpuHandle.ptr);
|
||||
EXPECT_EQ(registration.texture.nativeHandle, hostPtr->nextRegistration.texture.nativeHandle);
|
||||
|
||||
compositor.FreeTextureDescriptor(registration);
|
||||
EXPECT_EQ(hostPtr->freeTextureDescriptorCount, 1);
|
||||
EXPECT_EQ(hostPtr->freedRegistration.cpuHandle.ptr, registration.cpuHandle.ptr);
|
||||
EXPECT_EQ(hostPtr->freedRegistration.gpuHandle.ptr, registration.gpuHandle.ptr);
|
||||
EXPECT_EQ(hostPtr->freedRegistration.texture.nativeHandle, registration.texture.nativeHandle);
|
||||
}
|
||||
|
||||
TEST(ImGuiWindowUICompositorTest, ShutdownClearsRendererBindingAndPreventsFurtherRender) {
|
||||
auto host = std::make_unique<RecordingHostCompositor>();
|
||||
RecordingHostCompositor* hostPtr = host.get();
|
||||
|
||||
ImGuiWindowUICompositor compositor(std::move(host));
|
||||
D3D12WindowRenderer renderer = {};
|
||||
ASSERT_TRUE(compositor.Initialize(MakeFakeHwnd(), renderer, {}));
|
||||
|
||||
bool firstUiRendered = false;
|
||||
compositor.RenderFrame(
|
||||
std::array<float, 4>{ 0.0f, 0.0f, 0.0f, 1.0f }.data(),
|
||||
[&]() { firstUiRendered = true; },
|
||||
{},
|
||||
{});
|
||||
EXPECT_TRUE(firstUiRendered);
|
||||
EXPECT_EQ(hostPtr->beginFrameCount, 1);
|
||||
EXPECT_EQ(hostPtr->endFrameCount, 1);
|
||||
|
||||
compositor.Shutdown();
|
||||
EXPECT_EQ(hostPtr->shutdownCount, 1);
|
||||
|
||||
bool secondUiRendered = false;
|
||||
compositor.RenderFrame(
|
||||
std::array<float, 4>{ 1.0f, 0.0f, 0.0f, 1.0f }.data(),
|
||||
[&]() { secondUiRendered = true; },
|
||||
{},
|
||||
{});
|
||||
EXPECT_FALSE(secondUiRendered);
|
||||
EXPECT_EQ(hostPtr->beginFrameCount, 1);
|
||||
EXPECT_EQ(hostPtr->endFrameCount, 1);
|
||||
}
|
||||
|
||||
TEST(ImGuiWindowUICompositorTest, NullHostCompositorReturnsSafeDefaults) {
|
||||
ImGuiWindowUICompositor compositor(std::unique_ptr<IEditorHostCompositor>{});
|
||||
D3D12WindowRenderer renderer = {};
|
||||
|
||||
bool configureFontsCalled = false;
|
||||
EXPECT_FALSE(compositor.Initialize(
|
||||
MakeFakeHwnd(),
|
||||
renderer,
|
||||
[&configureFontsCalled]() { configureFontsCalled = true; }));
|
||||
EXPECT_FALSE(configureFontsCalled);
|
||||
EXPECT_FALSE(compositor.HandleWindowMessage(MakeFakeHwnd(), WM_CLOSE, 0u, 0u));
|
||||
|
||||
UITextureRegistration registration = {};
|
||||
EXPECT_FALSE(compositor.CreateTextureDescriptor(nullptr, nullptr, registration));
|
||||
|
||||
bool uiRendered = false;
|
||||
compositor.RenderFrame(
|
||||
std::array<float, 4>{ 0.0f, 0.0f, 0.0f, 1.0f }.data(),
|
||||
[&]() { uiRendered = true; },
|
||||
{},
|
||||
{});
|
||||
EXPECT_FALSE(uiRendered);
|
||||
|
||||
compositor.Shutdown();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user