#include #include "XCUIBackend/NativeXCUIPanelCanvasHost.h" namespace { using XCEngine::Editor::XCUIBackend::CreateNativeXCUIPanelCanvasHost; using XCEngine::Editor::XCUIBackend::IXCUIPanelCanvasHost; using XCEngine::Editor::XCUIBackend::NativeXCUIPanelCanvasHost; using XCEngine::Editor::XCUIBackend::XCUIPanelCanvasFrameSnapshot; using XCEngine::Editor::XCUIBackend::XCUIPanelCanvasHostBackend; using XCEngine::Editor::XCUIBackend::XCUIPanelCanvasHostCapabilities; using XCEngine::Editor::XCUIBackend::XCUIPanelCanvasRequest; using XCEngine::Editor::XCUIBackend::XCUIPanelCanvasSession; XCEngine::UI::UITextureHandle MakeSurfaceTextureHandle(std::uintptr_t nativeHandle, std::uint32_t width, std::uint32_t height) { XCEngine::UI::UITextureHandle texture = {}; texture.nativeHandle = nativeHandle; texture.width = width; texture.height = height; texture.kind = XCEngine::UI::UITextureHandleKind::ShaderResourceView; return texture; } TEST(NativeXCUIPanelCanvasHostTest, FactoryReportsNativeBackendCapabilitiesAndNoSnapshotBeforeBegin) { std::unique_ptr host = CreateNativeXCUIPanelCanvasHost(); ASSERT_NE(host, nullptr); EXPECT_STREQ(host->GetDebugName(), "NativeXCUIPanelCanvasHost"); EXPECT_EQ(host->GetBackend(), XCUIPanelCanvasHostBackend::Native); const XCUIPanelCanvasHostCapabilities capabilities = host->GetCapabilities(); EXPECT_FALSE(capabilities.supportsPointerHitTesting); EXPECT_TRUE(capabilities.supportsHostedSurfaceImages); EXPECT_TRUE(capabilities.supportsPrimitiveOverlays); EXPECT_TRUE(capabilities.supportsExternallyDrivenSession); XCUIPanelCanvasFrameSnapshot snapshot = {}; EXPECT_FALSE(host->TryGetLatestFrameSnapshot(snapshot)); EXPECT_TRUE(snapshot.childId.empty()); EXPECT_EQ(snapshot.overlayDrawData.GetDrawListCount(), 0u); } TEST(NativeXCUIPanelCanvasHostTest, BeginCanvasWithConfiguredSessionCapturesSnapshotAndOverlayCommands) { NativeXCUIPanelCanvasHost host = {}; XCUIPanelCanvasSession configuredSession = {}; configuredSession.hostRect = XCEngine::UI::UIRect(20.0f, 30.0f, 640.0f, 360.0f); configuredSession.canvasRect = XCEngine::UI::UIRect(20.0f, 72.0f, 640.0f, 318.0f); configuredSession.pointerPosition = XCEngine::UI::UIPoint(128.0f, 144.0f); configuredSession.validCanvas = true; configuredSession.hovered = true; configuredSession.windowFocused = true; host.SetCanvasSession(configuredSession); XCUIPanelCanvasRequest request = {}; request.childId = "NativeDemoCanvas"; request.height = 360.0f; request.topInset = 42.0f; request.placeholderTitle = "Placeholder"; request.placeholderSubtitle = "Native host placeholder"; request.badgeTitle = "XCUI Demo"; request.badgeSubtitle = "native path"; const XCUIPanelCanvasSession session = host.BeginCanvas(request); EXPECT_FLOAT_EQ(session.hostRect.x, configuredSession.hostRect.x); EXPECT_FLOAT_EQ(session.hostRect.y, configuredSession.hostRect.y); EXPECT_FLOAT_EQ(session.canvasRect.x, configuredSession.canvasRect.x); EXPECT_FLOAT_EQ(session.canvasRect.y, configuredSession.canvasRect.y); EXPECT_TRUE(session.validCanvas); EXPECT_TRUE(session.hovered); EXPECT_TRUE(session.windowFocused); host.DrawFilledRect( XCEngine::UI::UIRect(40.0f, 90.0f, 120.0f, 60.0f), XCEngine::UI::UIColor(0.8f, 0.2f, 0.1f, 1.0f), 6.0f); host.DrawOutlineRect( XCEngine::UI::UIRect(42.0f, 92.0f, 118.0f, 58.0f), XCEngine::UI::UIColor(0.2f, 0.8f, 0.3f, 1.0f), 2.0f, 4.0f); host.DrawText( XCEngine::UI::UIPoint(48.0f, 104.0f), "hello native", XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f), 16.0f); host.EndCanvas(); XCUIPanelCanvasFrameSnapshot snapshot = {}; ASSERT_TRUE(host.TryGetLatestFrameSnapshot(snapshot)); EXPECT_EQ(snapshot.childId, "NativeDemoCanvas"); EXPECT_FALSE(snapshot.showingSurfaceImage); EXPECT_TRUE(snapshot.drawPreviewFrame); EXPECT_EQ(snapshot.placeholderTitle, "Placeholder"); EXPECT_EQ(snapshot.placeholderSubtitle, "Native host placeholder"); EXPECT_EQ(snapshot.badgeTitle, "XCUI Demo"); EXPECT_EQ(snapshot.badgeSubtitle, "native path"); EXPECT_TRUE(snapshot.session.validCanvas); EXPECT_EQ(snapshot.overlayDrawData.GetDrawListCount(), 1u); EXPECT_EQ(snapshot.overlayDrawData.GetDrawLists().front().GetDebugName(), "NativeDemoCanvas.overlay"); EXPECT_EQ(snapshot.overlayDrawData.GetTotalCommandCount(), 14u); EXPECT_EQ( snapshot.overlayDrawData.GetDrawLists().front().GetCommands().front().type, XCEngine::UI::UIDrawCommandType::PushClipRect); EXPECT_EQ( snapshot.overlayDrawData.GetDrawLists().front().GetCommands().back().type, XCEngine::UI::UIDrawCommandType::PopClipRect); } TEST(NativeXCUIPanelCanvasHostTest, SurfaceImagePathCapturesSurfaceAndPreviewFrameWithoutPlaceholder) { NativeXCUIPanelCanvasHost host = {}; XCUIPanelCanvasSession configuredSession = {}; configuredSession.hostRect = XCEngine::UI::UIRect(0.0f, 0.0f, 800.0f, 480.0f); configuredSession.canvasRect = XCEngine::UI::UIRect(0.0f, 0.0f, 800.0f, 480.0f); configuredSession.validCanvas = true; host.SetCanvasSession(configuredSession); XCUIPanelCanvasRequest request = {}; request.childId = "NativeSurfaceCanvas"; request.showSurfaceImage = true; request.drawPreviewFrame = true; request.surfaceImage.texture = MakeSurfaceTextureHandle(17u, 1024u, 512u); request.surfaceImage.surfaceWidth = 1024u; request.surfaceImage.surfaceHeight = 512u; request.surfaceImage.uvMin = XCEngine::UI::UIPoint(0.0f, 0.0f); request.surfaceImage.uvMax = XCEngine::UI::UIPoint(1.0f, 1.0f); const XCUIPanelCanvasSession session = host.BeginCanvas(request); EXPECT_TRUE(session.validCanvas); host.EndCanvas(); XCUIPanelCanvasFrameSnapshot snapshot = {}; ASSERT_TRUE(host.TryGetLatestFrameSnapshot(snapshot)); EXPECT_TRUE(snapshot.showingSurfaceImage); EXPECT_TRUE(snapshot.surfaceImage.IsValid()); EXPECT_EQ(snapshot.surfaceImage.texture.nativeHandle, 17u); EXPECT_EQ(snapshot.overlayDrawData.GetDrawListCount(), 1u); EXPECT_EQ(snapshot.overlayDrawData.GetTotalCommandCount(), 3u); const auto& commands = snapshot.overlayDrawData.GetDrawLists().front().GetCommands(); ASSERT_EQ(commands.size(), 3u); EXPECT_EQ(commands[0].type, XCEngine::UI::UIDrawCommandType::PushClipRect); EXPECT_EQ(commands[1].type, XCEngine::UI::UIDrawCommandType::RectOutline); EXPECT_EQ(commands[2].type, XCEngine::UI::UIDrawCommandType::PopClipRect); } TEST(NativeXCUIPanelCanvasHostTest, ClearingConfiguredSessionFallsBackToPassiveSnapshot) { NativeXCUIPanelCanvasHost host = {}; XCUIPanelCanvasSession configuredSession = {}; configuredSession.hostRect = XCEngine::UI::UIRect(4.0f, 5.0f, 320.0f, 240.0f); configuredSession.canvasRect = XCEngine::UI::UIRect(4.0f, 25.0f, 320.0f, 220.0f); configuredSession.validCanvas = true; host.SetCanvasSession(configuredSession); ASSERT_TRUE(host.HasConfiguredSession()); host.ClearCanvasSession(); EXPECT_FALSE(host.HasConfiguredSession()); XCUIPanelCanvasRequest request = {}; request.height = 180.0f; request.topInset = 24.0f; const XCUIPanelCanvasSession session = host.BeginCanvas(request); host.EndCanvas(); EXPECT_FALSE(session.validCanvas); EXPECT_FLOAT_EQ(session.hostRect.height, 180.0f); EXPECT_FLOAT_EQ(session.canvasRect.y, 24.0f); EXPECT_FLOAT_EQ(session.canvasRect.height, 156.0f); XCUIPanelCanvasFrameSnapshot snapshot = {}; ASSERT_TRUE(host.TryGetLatestFrameSnapshot(snapshot)); EXPECT_FALSE(snapshot.session.validCanvas); EXPECT_EQ(snapshot.overlayDrawData.GetDrawListCount(), 0u); } } // namespace