#include #include "XCUIBackend/XCUILayoutLabRuntime.h" #include #include #include namespace { using XCEngine::UI::UIDrawCommand; using XCEngine::UI::UIDrawCommandType; XCEngine::Editor::XCUIBackend::XCUILayoutLabInputState BuildInputState( float width = 960.0f, float height = 640.0f) { XCEngine::Editor::XCUIBackend::XCUILayoutLabInputState input = {}; input.canvasRect = XCEngine::UI::UIRect(0.0f, 0.0f, width, height); input.pointerPosition = XCEngine::UI::UIPoint(width * 0.5f, height * 0.5f); input.pointerInside = true; return input; } std::vector CollectTextCommands(const XCEngine::UI::UIDrawData& drawData) { std::vector textCommands = {}; for (const XCEngine::UI::UIDrawList& drawList : drawData.GetDrawLists()) { for (const UIDrawCommand& command : drawList.GetCommands()) { if (command.type == UIDrawCommandType::Text) { textCommands.push_back(&command); } } } return textCommands; } const UIDrawCommand* FindTextCommand( const XCEngine::UI::UIDrawData& drawData, const std::string& text) { for (const XCEngine::UI::UIDrawList& drawList : drawData.GetDrawLists()) { for (const UIDrawCommand& command : drawList.GetCommands()) { if (command.type == UIDrawCommandType::Text && command.text == text) { return &command; } } } return nullptr; } std::size_t CountCommandsOfType( const XCEngine::UI::UIDrawData& drawData, UIDrawCommandType type) { std::size_t count = 0; for (const XCEngine::UI::UIDrawList& drawList : drawData.GetDrawLists()) { for (const UIDrawCommand& command : drawList.GetCommands()) { if (command.type == type) { ++count; } } } return count; } } // namespace TEST(NewEditorXCUILayoutLabRuntimeTest, UpdateBuildsLayoutSmokeFrame) { XCEngine::Editor::XCUIBackend::XCUILayoutLabRuntime runtime; const bool reloadSucceeded = runtime.ReloadDocuments(); const auto& frame = runtime.Update(BuildInputState()); EXPECT_EQ(frame.stats.documentsReady, reloadSucceeded); EXPECT_EQ(frame.stats.drawListCount, frame.drawData.GetDrawListCount()); EXPECT_EQ(frame.stats.commandCount, frame.drawData.GetTotalCommandCount()); if (frame.stats.documentsReady) { EXPECT_GT(frame.stats.drawListCount, 0u); EXPECT_GT(frame.stats.commandCount, 0u); EXPECT_GE(frame.stats.rowCount, 1u); EXPECT_GE(frame.stats.columnCount, 1u); EXPECT_GE(frame.stats.overlayCount, 1u); EXPECT_GE(frame.stats.scrollViewCount, 2u); XCEngine::UI::UIRect heroRect = {}; EXPECT_TRUE(runtime.TryGetElementRect("heroCard", heroRect)); EXPECT_GT(heroRect.width, 0.0f); EXPECT_GT(heroRect.height, 0.0f); } else { EXPECT_FALSE(frame.stats.statusMessage.empty()); } } TEST(NewEditorXCUILayoutLabRuntimeTest, FrameIncludesTextCommandsWithThemeFontSizes) { XCEngine::Editor::XCUIBackend::XCUILayoutLabRuntime runtime; ASSERT_TRUE(runtime.ReloadDocuments()); const auto& frame = runtime.Update(BuildInputState()); ASSERT_TRUE(frame.stats.documentsReady); const std::vector textCommands = CollectTextCommands(frame.drawData); ASSERT_FALSE(textCommands.empty()); for (const UIDrawCommand* command : textCommands) { ASSERT_NE(command, nullptr); EXPECT_FALSE(command->text.empty()); EXPECT_GT(command->fontSize, 0.0f); } const UIDrawCommand* titleCommand = FindTextCommand(frame.drawData, "XCUI Layout Lab"); ASSERT_NE(titleCommand, nullptr); EXPECT_FLOAT_EQ(titleCommand->fontSize, 16.0f); const UIDrawCommand* subtitleCommand = FindTextCommand( frame.drawData, "Editor-style panels with overlay and scroll semantics."); ASSERT_NE(subtitleCommand, nullptr); EXPECT_FLOAT_EQ(subtitleCommand->fontSize, 13.0f); } TEST(NewEditorXCUILayoutLabRuntimeTest, HoverProbeResolvesTrackedElementRect) { XCEngine::Editor::XCUIBackend::XCUILayoutLabRuntime runtime; ASSERT_TRUE(runtime.ReloadDocuments()); const auto& baseline = runtime.Update(BuildInputState()); ASSERT_TRUE(baseline.stats.documentsReady); XCEngine::UI::UIRect probeRect = {}; ASSERT_TRUE(runtime.TryGetElementRect("assetLighting", probeRect)); ASSERT_GT(probeRect.width, 0.0f); ASSERT_GT(probeRect.height, 0.0f); XCEngine::Editor::XCUIBackend::XCUILayoutLabInputState input = BuildInputState(); input.pointerPosition = XCEngine::UI::UIPoint( probeRect.x + probeRect.width * 0.5f, probeRect.y + probeRect.height * 0.5f); const auto& frame = runtime.Update(input); ASSERT_TRUE(frame.stats.documentsReady); EXPECT_FALSE(frame.stats.hoveredElementId.empty()); XCEngine::UI::UIRect hoveredRect = {}; EXPECT_TRUE(runtime.TryGetElementRect(frame.stats.hoveredElementId, hoveredRect)); EXPECT_GT(hoveredRect.width, 0.0f); EXPECT_GT(hoveredRect.height, 0.0f); } TEST(NewEditorXCUILayoutLabRuntimeTest, ScrollViewOffsetsContentAndAddsNestedClips) { XCEngine::Editor::XCUIBackend::XCUILayoutLabRuntime runtime; ASSERT_TRUE(runtime.ReloadDocuments()); const auto& frame = runtime.Update(BuildInputState()); ASSERT_TRUE(frame.stats.documentsReady); XCEngine::UI::UIRect assetListRect = {}; XCEngine::UI::UIRect headerRect = {}; XCEngine::UI::UIRect visibleItemRect = {}; ASSERT_TRUE(runtime.TryGetElementRect("assetList", assetListRect)); ASSERT_TRUE(runtime.TryGetElementRect("assetListHeader", headerRect)); ASSERT_TRUE(runtime.TryGetElementRect("assetLighting", visibleItemRect)); EXPECT_LT(headerRect.y, assetListRect.y); EXPECT_GT(visibleItemRect.y, assetListRect.y); EXPECT_LT(visibleItemRect.y, assetListRect.y + assetListRect.height); EXPECT_EQ( CountCommandsOfType(frame.drawData, UIDrawCommandType::PushClipRect), frame.stats.clipPushCommandCount); EXPECT_EQ( CountCommandsOfType(frame.drawData, UIDrawCommandType::PopClipRect), frame.stats.clipPopCommandCount); EXPECT_GE(frame.stats.clipPushCommandCount, 3u); EXPECT_GE(frame.stats.clipPopCommandCount, 3u); } TEST(NewEditorXCUILayoutLabRuntimeTest, HoverIgnoresClippedScrollViewContent) { XCEngine::Editor::XCUIBackend::XCUILayoutLabRuntime runtime; ASSERT_TRUE(runtime.ReloadDocuments()); const auto& baseline = runtime.Update(BuildInputState()); ASSERT_TRUE(baseline.stats.documentsReady); XCEngine::UI::UIRect assetListRect = {}; ASSERT_TRUE(runtime.TryGetElementRect("assetList", assetListRect)); XCEngine::Editor::XCUIBackend::XCUILayoutLabInputState input = BuildInputState(); input.pointerPosition = XCEngine::UI::UIPoint( assetListRect.x + assetListRect.width * 0.5f, assetListRect.y + assetListRect.height + 6.0f); const auto& frame = runtime.Update(input); ASSERT_TRUE(frame.stats.documentsReady); EXPECT_TRUE(frame.stats.hoveredElementId.empty()); }