Add XCUI new editor sandbox phase 1
This commit is contained in:
508
tests/NewEditor/CMakeLists.txt
Normal file
508
tests/NewEditor/CMakeLists.txt
Normal file
@@ -0,0 +1,508 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
project(XCEngine_NewEditorTests)
|
||||
|
||||
file(TO_CMAKE_PATH "${CMAKE_SOURCE_DIR}" XCENGINE_TEST_REPO_ROOT_CMAKE)
|
||||
|
||||
function(xcengine_configure_new_editor_test_target target_name)
|
||||
if(MSVC)
|
||||
set_target_properties(${target_name} PROPERTIES
|
||||
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
|
||||
COMPILE_PDB_NAME "${target_name}-compile"
|
||||
COMPILE_PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb"
|
||||
COMPILE_PDB_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/Debug"
|
||||
COMPILE_PDB_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/Release"
|
||||
COMPILE_PDB_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/MinSizeRel"
|
||||
COMPILE_PDB_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/RelWithDebInfo"
|
||||
VS_GLOBAL_UseMultiToolTask "false"
|
||||
)
|
||||
target_compile_options(${target_name} PRIVATE /FS /utf-8)
|
||||
target_link_options(${target_name} PRIVATE
|
||||
$<$<CONFIG:Debug,RelWithDebInfo>:/INCREMENTAL:NO>)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(xcengine_discover_new_editor_gtests target_name)
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(${target_name}
|
||||
DISCOVERY_MODE PRE_TEST)
|
||||
endfunction()
|
||||
|
||||
set(NEW_EDITOR_RUNTIME_HEADER
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIDemoRuntime.h
|
||||
)
|
||||
set(NEW_EDITOR_RUNTIME_SOURCE
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIDemoRuntime.cpp
|
||||
)
|
||||
set(NEW_EDITOR_LAYOUT_LAB_RUNTIME_HEADER
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUILayoutLabRuntime.h
|
||||
)
|
||||
set(NEW_EDITOR_LAYOUT_LAB_RUNTIME_SOURCE
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUILayoutLabRuntime.cpp
|
||||
)
|
||||
set(NEW_EDITOR_ASSET_DOCUMENT_SOURCE_HEADER
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIAssetDocumentSource.h
|
||||
)
|
||||
set(NEW_EDITOR_ASSET_DOCUMENT_SOURCE
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIAssetDocumentSource.cpp
|
||||
)
|
||||
set(NEW_EDITOR_UI_DOCUMENT_COMPILER_SOURCE
|
||||
${CMAKE_SOURCE_DIR}/engine/src/Resources/UI/UIDocumentCompiler.cpp
|
||||
)
|
||||
set(NEW_EDITOR_BACKEND_HEADER
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/ImGuiTransitionBackend.h
|
||||
)
|
||||
set(NEW_EDITOR_INPUT_BRIDGE_HEADER
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIInputBridge.h
|
||||
)
|
||||
set(NEW_EDITOR_INPUT_BRIDGE_SOURCE
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIInputBridge.cpp
|
||||
)
|
||||
set(NEW_EDITOR_IMGUI_INPUT_ADAPTER_HEADER
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/ImGuiXCUIInputAdapter.h
|
||||
)
|
||||
set(NEW_EDITOR_IMGUI_INPUT_ADAPTER_SOURCE
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/ImGuiXCUIInputAdapter.cpp
|
||||
)
|
||||
set(NEW_EDITOR_RHI_COMMAND_SUPPORT_HEADER
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIRHICommandSupport.h
|
||||
)
|
||||
set(NEW_EDITOR_RHI_COMMAND_COMPILER_HEADER
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIRHICommandCompiler.h
|
||||
)
|
||||
set(NEW_EDITOR_RHI_COMMAND_COMPILER_SOURCE
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIRHICommandCompiler.cpp
|
||||
)
|
||||
set(NEW_EDITOR_RHI_RENDER_BACKEND_HEADER
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIRHIRenderBackend.h
|
||||
)
|
||||
set(NEW_EDITOR_RHI_RENDER_BACKEND_SOURCE
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIRHIRenderBackend.cpp
|
||||
)
|
||||
set(NEW_EDITOR_IMGUI_TEXT_ATLAS_PROVIDER_HEADER
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/ImGuiTextAtlasProvider.h
|
||||
)
|
||||
set(NEW_EDITOR_IMGUI_TEXT_ATLAS_PROVIDER_SOURCE
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/ImGuiTextAtlasProvider.cpp
|
||||
)
|
||||
set(NEW_EDITOR_FONT_SETUP_HEADER
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIEditorFontSetup.h
|
||||
)
|
||||
set(NEW_EDITOR_FONT_SETUP_SOURCE
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIEditorFontSetup.cpp
|
||||
)
|
||||
set(NEW_EDITOR_STANDALONE_TEXT_ATLAS_PROVIDER_HEADER
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIStandaloneTextAtlasProvider.h
|
||||
)
|
||||
set(NEW_EDITOR_STANDALONE_TEXT_ATLAS_PROVIDER_SOURCE
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIStandaloneTextAtlasProvider.cpp
|
||||
)
|
||||
set(NEW_EDITOR_HOSTED_PREVIEW_PRESENTER_HEADER
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIHostedPreviewPresenter.h
|
||||
)
|
||||
set(NEW_EDITOR_NATIVE_BACKDROP_RENDERER_HEADER
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src/Rendering/MainWindowNativeBackdropRenderer.h
|
||||
)
|
||||
|
||||
if(EXISTS "${NEW_EDITOR_RUNTIME_HEADER}" AND
|
||||
EXISTS "${NEW_EDITOR_RUNTIME_SOURCE}" AND
|
||||
EXISTS "${NEW_EDITOR_ASSET_DOCUMENT_SOURCE_HEADER}" AND
|
||||
EXISTS "${NEW_EDITOR_ASSET_DOCUMENT_SOURCE}")
|
||||
add_executable(new_editor_xcui_demo_runtime_tests
|
||||
test_xcui_demo_runtime.cpp
|
||||
${NEW_EDITOR_RUNTIME_SOURCE}
|
||||
${NEW_EDITOR_ASSET_DOCUMENT_SOURCE}
|
||||
)
|
||||
|
||||
xcengine_configure_new_editor_test_target(new_editor_xcui_demo_runtime_tests)
|
||||
|
||||
target_link_libraries(new_editor_xcui_demo_runtime_tests
|
||||
PRIVATE
|
||||
XCEngine
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
target_include_directories(new_editor_xcui_demo_runtime_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src
|
||||
)
|
||||
target_compile_definitions(new_editor_xcui_demo_runtime_tests PRIVATE
|
||||
XCENGINE_NEW_EDITOR_REPO_ROOT="${XCENGINE_TEST_REPO_ROOT_CMAKE}"
|
||||
)
|
||||
|
||||
xcengine_discover_new_editor_gtests(new_editor_xcui_demo_runtime_tests)
|
||||
else()
|
||||
message(STATUS "Skipping new_editor_xcui_demo_runtime_tests because XCUIDemoRuntime files are missing.")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${NEW_EDITOR_ASSET_DOCUMENT_SOURCE_HEADER}" AND
|
||||
EXISTS "${NEW_EDITOR_ASSET_DOCUMENT_SOURCE}" AND
|
||||
EXISTS "${NEW_EDITOR_UI_DOCUMENT_COMPILER_SOURCE}" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test_xcui_asset_document_source.cpp")
|
||||
add_executable(new_editor_xcui_asset_document_source_tests
|
||||
test_xcui_asset_document_source.cpp
|
||||
${NEW_EDITOR_ASSET_DOCUMENT_SOURCE}
|
||||
${NEW_EDITOR_UI_DOCUMENT_COMPILER_SOURCE}
|
||||
)
|
||||
|
||||
xcengine_configure_new_editor_test_target(new_editor_xcui_asset_document_source_tests)
|
||||
|
||||
target_link_libraries(new_editor_xcui_asset_document_source_tests
|
||||
PRIVATE
|
||||
XCEngine
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
target_include_directories(new_editor_xcui_asset_document_source_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src
|
||||
)
|
||||
target_compile_definitions(new_editor_xcui_asset_document_source_tests PRIVATE
|
||||
XCENGINE_NEW_EDITOR_REPO_ROOT="${XCENGINE_TEST_REPO_ROOT_CMAKE}"
|
||||
)
|
||||
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(new_editor_xcui_asset_document_source_tests)
|
||||
else()
|
||||
message(STATUS "Skipping new_editor_xcui_asset_document_source_tests because asset document source files or the test source are missing.")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${NEW_EDITOR_LAYOUT_LAB_RUNTIME_HEADER}" AND
|
||||
EXISTS "${NEW_EDITOR_LAYOUT_LAB_RUNTIME_SOURCE}" AND
|
||||
EXISTS "${NEW_EDITOR_ASSET_DOCUMENT_SOURCE_HEADER}" AND
|
||||
EXISTS "${NEW_EDITOR_ASSET_DOCUMENT_SOURCE}")
|
||||
add_executable(new_editor_xcui_layout_lab_runtime_tests
|
||||
test_xcui_layout_lab_runtime.cpp
|
||||
${NEW_EDITOR_LAYOUT_LAB_RUNTIME_SOURCE}
|
||||
${NEW_EDITOR_ASSET_DOCUMENT_SOURCE}
|
||||
)
|
||||
|
||||
xcengine_configure_new_editor_test_target(new_editor_xcui_layout_lab_runtime_tests)
|
||||
|
||||
target_link_libraries(new_editor_xcui_layout_lab_runtime_tests
|
||||
PRIVATE
|
||||
XCEngine
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
target_include_directories(new_editor_xcui_layout_lab_runtime_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src
|
||||
)
|
||||
target_compile_definitions(new_editor_xcui_layout_lab_runtime_tests PRIVATE
|
||||
XCENGINE_NEW_EDITOR_REPO_ROOT="${XCENGINE_TEST_REPO_ROOT_CMAKE}"
|
||||
)
|
||||
|
||||
xcengine_discover_new_editor_gtests(new_editor_xcui_layout_lab_runtime_tests)
|
||||
else()
|
||||
message(STATUS "Skipping new_editor_xcui_layout_lab_runtime_tests because XCUILayoutLabRuntime files are missing.")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${NEW_EDITOR_BACKEND_HEADER}" AND EXISTS "${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui.cpp")
|
||||
add_executable(new_editor_imgui_transition_backend_tests
|
||||
test_new_editor_imgui_transition_backend.cpp
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui.cpp
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_draw.cpp
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_tables.cpp
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_widgets.cpp
|
||||
)
|
||||
|
||||
xcengine_configure_new_editor_test_target(new_editor_imgui_transition_backend_tests)
|
||||
|
||||
target_link_libraries(new_editor_imgui_transition_backend_tests
|
||||
PRIVATE
|
||||
XCEngine
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
user32
|
||||
comdlg32
|
||||
)
|
||||
|
||||
target_include_directories(new_editor_imgui_transition_backend_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/backends
|
||||
)
|
||||
|
||||
xcengine_discover_new_editor_gtests(new_editor_imgui_transition_backend_tests)
|
||||
else()
|
||||
message(STATUS "Skipping new_editor_imgui_transition_backend_tests because backend or ImGui sources are missing.")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${NEW_EDITOR_HOSTED_PREVIEW_PRESENTER_HEADER}" AND EXISTS "${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui.cpp")
|
||||
add_executable(new_editor_xcui_hosted_preview_presenter_tests
|
||||
test_xcui_hosted_preview_presenter.cpp
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui.cpp
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_draw.cpp
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_tables.cpp
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_widgets.cpp
|
||||
)
|
||||
|
||||
xcengine_configure_new_editor_test_target(new_editor_xcui_hosted_preview_presenter_tests)
|
||||
|
||||
target_link_libraries(new_editor_xcui_hosted_preview_presenter_tests
|
||||
PRIVATE
|
||||
XCEngine
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
user32
|
||||
comdlg32
|
||||
)
|
||||
|
||||
target_include_directories(new_editor_xcui_hosted_preview_presenter_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/backends
|
||||
)
|
||||
|
||||
xcengine_discover_new_editor_gtests(new_editor_xcui_hosted_preview_presenter_tests)
|
||||
else()
|
||||
message(STATUS "Skipping new_editor_xcui_hosted_preview_presenter_tests because presenter header or ImGui sources are missing.")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${NEW_EDITOR_NATIVE_BACKDROP_RENDERER_HEADER}")
|
||||
add_executable(new_editor_native_backdrop_renderer_api_tests
|
||||
test_main_window_native_backdrop_renderer_api.cpp
|
||||
)
|
||||
|
||||
xcengine_configure_new_editor_test_target(new_editor_native_backdrop_renderer_api_tests)
|
||||
|
||||
target_link_libraries(new_editor_native_backdrop_renderer_api_tests
|
||||
PRIVATE
|
||||
XCEngine
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
target_include_directories(new_editor_native_backdrop_renderer_api_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src
|
||||
)
|
||||
|
||||
xcengine_discover_new_editor_gtests(new_editor_native_backdrop_renderer_api_tests)
|
||||
else()
|
||||
message(STATUS "Skipping new_editor_native_backdrop_renderer_api_tests because renderer header is missing.")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${NEW_EDITOR_INPUT_BRIDGE_HEADER}" AND
|
||||
EXISTS "${NEW_EDITOR_INPUT_BRIDGE_SOURCE}")
|
||||
add_executable(new_editor_xcui_input_bridge_tests
|
||||
test_xcui_input_bridge.cpp
|
||||
${NEW_EDITOR_INPUT_BRIDGE_SOURCE}
|
||||
)
|
||||
|
||||
xcengine_configure_new_editor_test_target(new_editor_xcui_input_bridge_tests)
|
||||
|
||||
target_link_libraries(new_editor_xcui_input_bridge_tests
|
||||
PRIVATE
|
||||
XCEngine
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
target_include_directories(new_editor_xcui_input_bridge_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/backends
|
||||
)
|
||||
|
||||
xcengine_discover_new_editor_gtests(new_editor_xcui_input_bridge_tests)
|
||||
else()
|
||||
message(STATUS "Skipping new_editor_xcui_input_bridge_tests because input bridge files are missing.")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${NEW_EDITOR_IMGUI_INPUT_ADAPTER_HEADER}" AND
|
||||
EXISTS "${NEW_EDITOR_IMGUI_INPUT_ADAPTER_SOURCE}" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test_imgui_xcui_input_adapter.cpp" AND
|
||||
EXISTS "${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui.cpp")
|
||||
add_executable(new_editor_imgui_xcui_input_adapter_tests
|
||||
test_imgui_xcui_input_adapter.cpp
|
||||
${NEW_EDITOR_IMGUI_INPUT_ADAPTER_SOURCE}
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui.cpp
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_draw.cpp
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_tables.cpp
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_widgets.cpp
|
||||
)
|
||||
|
||||
xcengine_configure_new_editor_test_target(new_editor_imgui_xcui_input_adapter_tests)
|
||||
|
||||
target_link_libraries(new_editor_imgui_xcui_input_adapter_tests
|
||||
PRIVATE
|
||||
XCEngine
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
user32
|
||||
comdlg32
|
||||
)
|
||||
|
||||
target_include_directories(new_editor_imgui_xcui_input_adapter_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/backends
|
||||
)
|
||||
|
||||
xcengine_discover_new_editor_gtests(new_editor_imgui_xcui_input_adapter_tests)
|
||||
else()
|
||||
message(STATUS "Skipping new_editor_imgui_xcui_input_adapter_tests because ImGui adapter files, test source, or ImGui sources are missing.")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${NEW_EDITOR_RHI_COMMAND_SUPPORT_HEADER}")
|
||||
add_executable(new_editor_xcui_rhi_command_support_tests
|
||||
test_xcui_rhi_command_support.cpp
|
||||
)
|
||||
|
||||
xcengine_configure_new_editor_test_target(new_editor_xcui_rhi_command_support_tests)
|
||||
|
||||
target_link_libraries(new_editor_xcui_rhi_command_support_tests
|
||||
PRIVATE
|
||||
XCEngine
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
target_include_directories(new_editor_xcui_rhi_command_support_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src
|
||||
)
|
||||
|
||||
xcengine_discover_new_editor_gtests(new_editor_xcui_rhi_command_support_tests)
|
||||
else()
|
||||
message(STATUS "Skipping new_editor_xcui_rhi_command_support_tests because support helper header is missing.")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${NEW_EDITOR_RHI_RENDER_BACKEND_HEADER}" AND
|
||||
EXISTS "${NEW_EDITOR_RHI_RENDER_BACKEND_SOURCE}" AND
|
||||
EXISTS "${NEW_EDITOR_RHI_COMMAND_COMPILER_HEADER}" AND
|
||||
EXISTS "${NEW_EDITOR_RHI_COMMAND_COMPILER_SOURCE}" AND
|
||||
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test_xcui_rhi_render_backend.cpp")
|
||||
add_executable(new_editor_xcui_rhi_render_backend_tests
|
||||
test_xcui_rhi_render_backend.cpp
|
||||
${NEW_EDITOR_RHI_RENDER_BACKEND_SOURCE}
|
||||
${NEW_EDITOR_RHI_COMMAND_COMPILER_SOURCE}
|
||||
)
|
||||
|
||||
xcengine_configure_new_editor_test_target(new_editor_xcui_rhi_render_backend_tests)
|
||||
|
||||
target_link_libraries(new_editor_xcui_rhi_render_backend_tests
|
||||
PRIVATE
|
||||
XCEngine
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
target_include_directories(new_editor_xcui_rhi_render_backend_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src
|
||||
)
|
||||
|
||||
xcengine_discover_new_editor_gtests(new_editor_xcui_rhi_render_backend_tests)
|
||||
else()
|
||||
message(STATUS "Skipping new_editor_xcui_rhi_render_backend_tests because backend/compiler sources or the test source are missing.")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${NEW_EDITOR_IMGUI_TEXT_ATLAS_PROVIDER_HEADER}" AND
|
||||
EXISTS "${NEW_EDITOR_IMGUI_TEXT_ATLAS_PROVIDER_SOURCE}" AND
|
||||
EXISTS "${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui.cpp")
|
||||
add_executable(new_editor_imgui_text_atlas_provider_tests
|
||||
test_imgui_text_atlas_provider.cpp
|
||||
${NEW_EDITOR_IMGUI_TEXT_ATLAS_PROVIDER_SOURCE}
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui.cpp
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_draw.cpp
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_tables.cpp
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_widgets.cpp
|
||||
)
|
||||
|
||||
xcengine_configure_new_editor_test_target(new_editor_imgui_text_atlas_provider_tests)
|
||||
|
||||
target_link_libraries(new_editor_imgui_text_atlas_provider_tests
|
||||
PRIVATE
|
||||
XCEngine
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
user32
|
||||
comdlg32
|
||||
)
|
||||
|
||||
target_include_directories(new_editor_imgui_text_atlas_provider_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/backends
|
||||
)
|
||||
|
||||
xcengine_discover_new_editor_gtests(new_editor_imgui_text_atlas_provider_tests)
|
||||
else()
|
||||
message(STATUS "Skipping new_editor_imgui_text_atlas_provider_tests because atlas provider or ImGui sources are missing.")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${NEW_EDITOR_STANDALONE_TEXT_ATLAS_PROVIDER_HEADER}" AND
|
||||
EXISTS "${NEW_EDITOR_STANDALONE_TEXT_ATLAS_PROVIDER_SOURCE}" AND
|
||||
EXISTS "${NEW_EDITOR_FONT_SETUP_HEADER}" AND
|
||||
EXISTS "${NEW_EDITOR_FONT_SETUP_SOURCE}" AND
|
||||
EXISTS "${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui.cpp")
|
||||
add_executable(new_editor_xcui_standalone_text_atlas_provider_tests
|
||||
test_xcui_standalone_text_atlas_provider.cpp
|
||||
${NEW_EDITOR_FONT_SETUP_SOURCE}
|
||||
${NEW_EDITOR_STANDALONE_TEXT_ATLAS_PROVIDER_SOURCE}
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui.cpp
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_draw.cpp
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_tables.cpp
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_widgets.cpp
|
||||
)
|
||||
|
||||
xcengine_configure_new_editor_test_target(new_editor_xcui_standalone_text_atlas_provider_tests)
|
||||
|
||||
target_link_libraries(new_editor_xcui_standalone_text_atlas_provider_tests
|
||||
PRIVATE
|
||||
XCEngine
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
user32
|
||||
comdlg32
|
||||
)
|
||||
|
||||
target_include_directories(new_editor_xcui_standalone_text_atlas_provider_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/backends
|
||||
)
|
||||
|
||||
xcengine_discover_new_editor_gtests(new_editor_xcui_standalone_text_atlas_provider_tests)
|
||||
else()
|
||||
message(STATUS "Skipping new_editor_xcui_standalone_text_atlas_provider_tests because standalone atlas provider, font setup, or ImGui sources are missing.")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${NEW_EDITOR_RHI_COMMAND_COMPILER_HEADER}" AND
|
||||
EXISTS "${NEW_EDITOR_RHI_COMMAND_COMPILER_SOURCE}")
|
||||
add_executable(new_editor_xcui_rhi_command_compiler_tests
|
||||
test_xcui_rhi_command_compiler.cpp
|
||||
${NEW_EDITOR_RHI_COMMAND_COMPILER_SOURCE}
|
||||
)
|
||||
|
||||
xcengine_configure_new_editor_test_target(new_editor_xcui_rhi_command_compiler_tests)
|
||||
|
||||
target_link_libraries(new_editor_xcui_rhi_command_compiler_tests
|
||||
PRIVATE
|
||||
XCEngine
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
target_include_directories(new_editor_xcui_rhi_command_compiler_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/new_editor/src
|
||||
)
|
||||
|
||||
xcengine_discover_new_editor_gtests(new_editor_xcui_rhi_command_compiler_tests)
|
||||
else()
|
||||
message(STATUS "Skipping new_editor_xcui_rhi_command_compiler_tests because compiler files are missing.")
|
||||
endif()
|
||||
90
tests/NewEditor/test_imgui_text_atlas_provider.cpp
Normal file
90
tests/NewEditor/test_imgui_text_atlas_provider.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "XCUIBackend/ImGuiTextAtlasProvider.h"
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::Editor::XCUIBackend::IXCUITextAtlasProvider;
|
||||
using XCEngine::Editor::XCUIBackend::ImGuiTextAtlasProvider;
|
||||
|
||||
class ImGuiContextScope {
|
||||
public:
|
||||
ImGuiContextScope() {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGui::StyleColorsDark();
|
||||
}
|
||||
|
||||
~ImGuiContextScope() {
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
};
|
||||
|
||||
void BuildDefaultFontAtlas() {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (io.Fonts->Fonts.empty()) {
|
||||
io.Fonts->AddFontDefault();
|
||||
}
|
||||
|
||||
unsigned char* pixels = nullptr;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
}
|
||||
|
||||
TEST(ImGuiTextAtlasProviderTest, ReturnsEmptyResultsWhenNoContextIsAvailable) {
|
||||
ImGuiTextAtlasProvider provider = {};
|
||||
IXCUITextAtlasProvider::AtlasTextureView atlasView = {};
|
||||
IXCUITextAtlasProvider::FontInfo fontInfo = {};
|
||||
IXCUITextAtlasProvider::BakedFontInfo bakedFontInfo = {};
|
||||
IXCUITextAtlasProvider::GlyphInfo glyphInfo = {};
|
||||
|
||||
EXPECT_EQ(provider.GetContext(), nullptr);
|
||||
EXPECT_FALSE(provider.GetAtlasTextureView(IXCUITextAtlasProvider::PixelFormat::RGBA32, atlasView));
|
||||
EXPECT_EQ(provider.GetFontCount(), 0u);
|
||||
EXPECT_FALSE(provider.GetDefaultFont().IsValid());
|
||||
EXPECT_FALSE(provider.GetFontInfo({}, fontInfo));
|
||||
EXPECT_FALSE(provider.GetBakedFontInfo({}, 14.0f, bakedFontInfo));
|
||||
EXPECT_FALSE(provider.FindGlyph({}, 14.0f, 'A', glyphInfo));
|
||||
}
|
||||
|
||||
TEST(ImGuiTextAtlasProviderTest, ExposesAtlasAndGlyphDataFromExplicitContext) {
|
||||
ImGuiContextScope contextScope;
|
||||
BuildDefaultFontAtlas();
|
||||
|
||||
ImGuiTextAtlasProvider provider(ImGui::GetCurrentContext());
|
||||
|
||||
IXCUITextAtlasProvider::AtlasTextureView atlasView = {};
|
||||
ASSERT_TRUE(provider.GetAtlasTextureView(IXCUITextAtlasProvider::PixelFormat::RGBA32, atlasView));
|
||||
EXPECT_TRUE(atlasView.IsValid());
|
||||
EXPECT_EQ(atlasView.format, IXCUITextAtlasProvider::PixelFormat::RGBA32);
|
||||
EXPECT_GT(atlasView.atlasStorageKey, 0u);
|
||||
EXPECT_GT(atlasView.pixelDataKey, 0u);
|
||||
|
||||
const IXCUITextAtlasProvider::FontHandle defaultFont = provider.GetDefaultFont();
|
||||
ASSERT_TRUE(defaultFont.IsValid());
|
||||
ASSERT_GE(provider.GetFontCount(), 1u);
|
||||
|
||||
IXCUITextAtlasProvider::FontInfo fontInfo = {};
|
||||
ASSERT_TRUE(provider.GetFontInfo(defaultFont, fontInfo));
|
||||
EXPECT_TRUE(fontInfo.handle.IsValid());
|
||||
EXPECT_GT(fontInfo.nominalSize, 0.0f);
|
||||
|
||||
IXCUITextAtlasProvider::BakedFontInfo bakedFontInfo = {};
|
||||
ASSERT_TRUE(provider.GetBakedFontInfo(defaultFont, 0.0f, bakedFontInfo));
|
||||
EXPECT_GT(bakedFontInfo.lineHeight, 0.0f);
|
||||
EXPECT_GT(bakedFontInfo.rasterizerDensity, 0.0f);
|
||||
|
||||
IXCUITextAtlasProvider::GlyphInfo glyphInfo = {};
|
||||
ASSERT_TRUE(provider.FindGlyph(defaultFont, 0.0f, 'A', glyphInfo));
|
||||
EXPECT_EQ(glyphInfo.requestedCodepoint, static_cast<std::uint32_t>('A'));
|
||||
EXPECT_GT(glyphInfo.advanceX, 0.0f);
|
||||
EXPECT_GE(glyphInfo.u1, glyphInfo.u0);
|
||||
EXPECT_GE(glyphInfo.v1, glyphInfo.v0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
88
tests/NewEditor/test_imgui_xcui_input_adapter.cpp
Normal file
88
tests/NewEditor/test_imgui_xcui_input_adapter.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "XCUIBackend/ImGuiXCUIInputAdapter.h"
|
||||
|
||||
#include <XCEngine/Input/InputTypes.h>
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::Editor::XCUIBackend::ImGuiXCUIInputAdapter;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIInputBridgeCaptureOptions;
|
||||
using XCEngine::Input::KeyCode;
|
||||
|
||||
class ImGuiContextScope {
|
||||
public:
|
||||
ImGuiContextScope() {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGui::StyleColorsDark();
|
||||
}
|
||||
|
||||
~ImGuiContextScope() {
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
};
|
||||
|
||||
void PrepareImGui(float width = 1024.0f, float height = 768.0f) {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.DisplaySize = ImVec2(width, height);
|
||||
io.DeltaTime = 1.0f / 60.0f;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(ImGuiXCUIInputAdapterTest, CaptureSnapshotMapsImGuiStateIntoXCUIFrameSnapshot) {
|
||||
ImGuiContextScope contextScope;
|
||||
PrepareImGui(800.0f, 600.0f);
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.WantCaptureMouse = true;
|
||||
io.WantCaptureKeyboard = true;
|
||||
io.WantTextInput = true;
|
||||
io.AddMousePosEvent(120.0f, 72.0f);
|
||||
io.AddMouseButtonEvent(0, true);
|
||||
io.AddMouseWheelEvent(0.0f, 1.0f);
|
||||
io.AddKeyEvent(ImGuiKey_LeftCtrl, true);
|
||||
io.AddKeyEvent(ImGuiKey_P, true);
|
||||
io.AddInputCharacter('p');
|
||||
|
||||
ImGui::NewFrame();
|
||||
|
||||
XCUIInputBridgeCaptureOptions options = {};
|
||||
options.pointerOffset = XCEngine::UI::UIPoint(20.0f, 12.0f);
|
||||
options.windowFocused = false;
|
||||
options.timestampNanoseconds = 99u;
|
||||
|
||||
const auto snapshot = ImGuiXCUIInputAdapter::CaptureSnapshot(io, options);
|
||||
|
||||
ImGui::EndFrame();
|
||||
|
||||
EXPECT_FLOAT_EQ(snapshot.pointerPosition.x, 100.0f);
|
||||
EXPECT_FLOAT_EQ(snapshot.pointerPosition.y, 60.0f);
|
||||
EXPECT_TRUE(snapshot.pointerInside);
|
||||
EXPECT_TRUE(snapshot.pointerButtonsDown[0]);
|
||||
EXPECT_FLOAT_EQ(snapshot.wheelDelta.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(snapshot.wheelDelta.y, 1.0f);
|
||||
EXPECT_TRUE(snapshot.modifiers.control);
|
||||
EXPECT_FALSE(snapshot.windowFocused);
|
||||
EXPECT_TRUE(snapshot.wantCaptureMouse);
|
||||
EXPECT_TRUE(snapshot.wantCaptureKeyboard);
|
||||
EXPECT_TRUE(snapshot.wantTextInput);
|
||||
EXPECT_EQ(snapshot.timestampNanoseconds, 99u);
|
||||
EXPECT_TRUE(snapshot.IsKeyDown(static_cast<std::int32_t>(KeyCode::LeftCtrl)));
|
||||
EXPECT_TRUE(snapshot.IsKeyDown(static_cast<std::int32_t>(KeyCode::P)));
|
||||
ASSERT_EQ(snapshot.characters.size(), 1u);
|
||||
EXPECT_EQ(snapshot.characters[0], static_cast<std::uint32_t>('p'));
|
||||
}
|
||||
|
||||
TEST(ImGuiXCUIInputAdapterTest, MapKeyCodeReturnsXCUIKeyCodesForNamedKeys) {
|
||||
EXPECT_EQ(
|
||||
ImGuiXCUIInputAdapter::MapKeyCode(ImGuiKey_A),
|
||||
static_cast<std::int32_t>(KeyCode::A));
|
||||
EXPECT_EQ(
|
||||
ImGuiXCUIInputAdapter::MapKeyCode(ImGuiKey_F12),
|
||||
static_cast<std::int32_t>(KeyCode::F12));
|
||||
EXPECT_EQ(
|
||||
ImGuiXCUIInputAdapter::MapKeyCode(ImGuiKey_None),
|
||||
static_cast<std::int32_t>(KeyCode::None));
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Rendering/MainWindowNativeBackdropRenderer.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::NewEditor::MainWindowNativeBackdropRenderer;
|
||||
using XCEngine::Rendering::RenderContext;
|
||||
using XCEngine::Rendering::RenderSurface;
|
||||
|
||||
TEST(NewEditorMainWindowNativeBackdropRendererApiTest, ExposesFrameStateAndSurfaceRenderEntry) {
|
||||
using FrameState = MainWindowNativeBackdropRenderer::FrameState;
|
||||
|
||||
static_assert(std::is_same_v<
|
||||
decltype(std::declval<FrameState>().elapsedSeconds),
|
||||
float>);
|
||||
static_assert(std::is_same_v<
|
||||
decltype(std::declval<FrameState>().pulseAccent),
|
||||
bool>);
|
||||
static_assert(std::is_same_v<
|
||||
decltype(std::declval<MainWindowNativeBackdropRenderer&>().Render(
|
||||
std::declval<const RenderContext&>(),
|
||||
std::declval<const RenderSurface&>(),
|
||||
std::declval<const FrameState&>())),
|
||||
bool>);
|
||||
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
306
tests/NewEditor/test_new_editor_imgui_transition_backend.cpp
Normal file
306
tests/NewEditor/test_new_editor_imgui_transition_backend.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "XCUIBackend/ImGuiTransitionBackend.h"
|
||||
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
namespace {
|
||||
|
||||
class ImGuiContextScope {
|
||||
public:
|
||||
ImGuiContextScope() {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGui::StyleColorsDark();
|
||||
}
|
||||
|
||||
~ImGuiContextScope() {
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
};
|
||||
|
||||
void PrepareImGui(float width = 1024.0f, float height = 768.0f) {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.DisplaySize = ImVec2(width, height);
|
||||
io.DeltaTime = 1.0f / 60.0f;
|
||||
unsigned char* fontPixels = nullptr;
|
||||
int fontWidth = 0;
|
||||
int fontHeight = 0;
|
||||
io.Fonts->GetTexDataAsRGBA32(&fontPixels, &fontWidth, &fontHeight);
|
||||
io.Fonts->SetTexID(static_cast<ImTextureID>(1));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(NewEditorImGuiTransitionBackendTest, BeginFrameClearsPendingQueue) {
|
||||
ImGuiContextScope contextScope;
|
||||
|
||||
XCEngine::Editor::XCUIBackend::ImGuiTransitionBackend backend = {};
|
||||
XCEngine::UI::UIDrawList drawList("Pending");
|
||||
drawList.AddFilledRect(
|
||||
XCEngine::UI::UIRect(0.0f, 0.0f, 20.0f, 20.0f),
|
||||
XCEngine::UI::UIColor(1.0f, 0.0f, 0.0f, 1.0f));
|
||||
|
||||
backend.BeginFrame();
|
||||
backend.Submit(drawList);
|
||||
ASSERT_EQ(backend.GetPendingDrawListCount(), 1u);
|
||||
ASSERT_EQ(backend.GetPendingCommandCount(), 1u);
|
||||
|
||||
backend.BeginFrame();
|
||||
EXPECT_EQ(backend.GetPendingDrawListCount(), 0u);
|
||||
EXPECT_EQ(backend.GetPendingCommandCount(), 0u);
|
||||
}
|
||||
|
||||
TEST(NewEditorImGuiTransitionBackendTest, EndFrameFlushesSubmittedCommands) {
|
||||
ImGuiContextScope contextScope;
|
||||
PrepareImGui();
|
||||
|
||||
XCEngine::Editor::XCUIBackend::ImGuiTransitionBackend backend = {};
|
||||
XCEngine::UI::UIDrawList drawList("Flush");
|
||||
drawList.PushClipRect(XCEngine::UI::UIRect(0.0f, 0.0f, 240.0f, 160.0f));
|
||||
drawList.AddFilledRect(
|
||||
XCEngine::UI::UIRect(10.0f, 10.0f, 70.0f, 45.0f),
|
||||
XCEngine::UI::UIColor(0.2f, 0.4f, 0.6f, 1.0f),
|
||||
6.0f);
|
||||
drawList.AddRectOutline(
|
||||
XCEngine::UI::UIRect(10.0f, 10.0f, 70.0f, 45.0f),
|
||||
XCEngine::UI::UIColor(0.9f, 0.9f, 0.9f, 1.0f),
|
||||
2.0f,
|
||||
6.0f);
|
||||
drawList.PopClipRect();
|
||||
|
||||
backend.BeginFrame();
|
||||
backend.Submit(drawList);
|
||||
|
||||
ImGui::NewFrame();
|
||||
ASSERT_TRUE(ImGui::Begin("NewEditorXCUIBackendTest"));
|
||||
ImDrawList* targetDrawList = ImGui::GetWindowDrawList();
|
||||
ASSERT_NE(targetDrawList, nullptr);
|
||||
|
||||
const bool flushed = backend.EndFrame(targetDrawList);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::EndFrame();
|
||||
|
||||
EXPECT_TRUE(flushed);
|
||||
EXPECT_EQ(backend.GetLastFlushedDrawListCount(), 1u);
|
||||
EXPECT_EQ(backend.GetLastFlushedCommandCount(), 4u);
|
||||
EXPECT_EQ(backend.GetPendingCommandCount(), 0u);
|
||||
EXPECT_GT(targetDrawList->VtxBuffer.Size, 0);
|
||||
EXPECT_GT(targetDrawList->CmdBuffer.Size, 0);
|
||||
}
|
||||
|
||||
TEST(NewEditorImGuiTransitionBackendTest, EndFrameFlushesMultipleDrawLists) {
|
||||
ImGuiContextScope contextScope;
|
||||
PrepareImGui(800.0f, 600.0f);
|
||||
|
||||
XCEngine::Editor::XCUIBackend::ImGuiTransitionBackend backend = {};
|
||||
|
||||
XCEngine::UI::UIDrawList firstDrawList("First");
|
||||
firstDrawList.AddFilledRect(
|
||||
XCEngine::UI::UIRect(8.0f, 8.0f, 40.0f, 30.0f),
|
||||
XCEngine::UI::UIColor(0.7f, 0.1f, 0.1f, 1.0f));
|
||||
|
||||
XCEngine::UI::UIDrawList secondDrawList("Second");
|
||||
secondDrawList.PushClipRect(XCEngine::UI::UIRect(0.0f, 0.0f, 300.0f, 200.0f));
|
||||
secondDrawList.AddRectOutline(
|
||||
XCEngine::UI::UIRect(24.0f, 26.0f, 52.0f, 28.0f),
|
||||
XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
1.5f,
|
||||
5.0f);
|
||||
secondDrawList.PopClipRect();
|
||||
|
||||
backend.BeginFrame();
|
||||
backend.Submit(firstDrawList);
|
||||
backend.Submit(std::move(secondDrawList));
|
||||
ASSERT_EQ(backend.GetPendingDrawListCount(), 2u);
|
||||
ASSERT_EQ(backend.GetPendingCommandCount(), 4u);
|
||||
|
||||
ImGui::NewFrame();
|
||||
ASSERT_TRUE(ImGui::Begin("NewEditorXCUIBackendMultiDrawList"));
|
||||
ImDrawList* targetDrawList = ImGui::GetWindowDrawList();
|
||||
ASSERT_NE(targetDrawList, nullptr);
|
||||
|
||||
const bool flushed = backend.EndFrame(targetDrawList);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::EndFrame();
|
||||
|
||||
EXPECT_TRUE(flushed);
|
||||
EXPECT_EQ(backend.GetLastFlushedDrawListCount(), 2u);
|
||||
EXPECT_EQ(backend.GetLastFlushedCommandCount(), 4u);
|
||||
EXPECT_EQ(backend.GetPendingDrawListCount(), 0u);
|
||||
EXPECT_EQ(backend.GetPendingCommandCount(), 0u);
|
||||
EXPECT_GT(targetDrawList->CmdBuffer.Size, 0);
|
||||
}
|
||||
|
||||
TEST(NewEditorImGuiTransitionBackendTest, EndFrameWithNoPendingDataLeavesFlushCountsAtZero) {
|
||||
ImGuiContextScope contextScope;
|
||||
PrepareImGui(640.0f, 480.0f);
|
||||
|
||||
XCEngine::Editor::XCUIBackend::ImGuiTransitionBackend backend = {};
|
||||
backend.BeginFrame();
|
||||
|
||||
ImGui::NewFrame();
|
||||
ASSERT_TRUE(ImGui::Begin("NewEditorXCUIBackendEmptyFrame"));
|
||||
ImDrawList* targetDrawList = ImGui::GetWindowDrawList();
|
||||
ASSERT_NE(targetDrawList, nullptr);
|
||||
|
||||
const bool flushed = backend.EndFrame(targetDrawList);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::EndFrame();
|
||||
|
||||
EXPECT_TRUE(flushed);
|
||||
EXPECT_EQ(backend.GetLastFlushedDrawListCount(), 0u);
|
||||
EXPECT_EQ(backend.GetLastFlushedCommandCount(), 0u);
|
||||
EXPECT_EQ(backend.GetPendingDrawListCount(), 0u);
|
||||
EXPECT_EQ(backend.GetPendingCommandCount(), 0u);
|
||||
}
|
||||
|
||||
TEST(NewEditorImGuiTransitionBackendTest, SubmitDrawDataAggregatesMultipleListsBeforeFlush) {
|
||||
ImGuiContextScope contextScope;
|
||||
PrepareImGui(720.0f, 512.0f);
|
||||
|
||||
XCEngine::Editor::XCUIBackend::ImGuiTransitionBackend backend = {};
|
||||
|
||||
XCEngine::UI::UIDrawData drawData = {};
|
||||
XCEngine::UI::UIDrawList& firstDrawList = drawData.EmplaceDrawList("First");
|
||||
firstDrawList.AddFilledRect(
|
||||
XCEngine::UI::UIRect(12.0f, 14.0f, 48.0f, 24.0f),
|
||||
XCEngine::UI::UIColor(0.4f, 0.5f, 0.9f, 1.0f));
|
||||
|
||||
XCEngine::UI::UIDrawList& secondDrawList = drawData.EmplaceDrawList("Second");
|
||||
secondDrawList.AddRectOutline(
|
||||
XCEngine::UI::UIRect(36.0f, 42.0f, 64.0f, 28.0f),
|
||||
XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
2.0f,
|
||||
3.0f);
|
||||
secondDrawList.AddText(
|
||||
XCEngine::UI::UIPoint(40.0f, 48.0f),
|
||||
"xcui",
|
||||
XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
14.0f);
|
||||
|
||||
backend.BeginFrame();
|
||||
backend.Submit(drawData);
|
||||
ASSERT_EQ(backend.GetPendingDrawListCount(), 2u);
|
||||
ASSERT_EQ(backend.GetPendingCommandCount(), 3u);
|
||||
|
||||
ImGui::NewFrame();
|
||||
ASSERT_TRUE(ImGui::Begin("NewEditorXCUIBackendDrawData"));
|
||||
ImDrawList* targetDrawList = ImGui::GetWindowDrawList();
|
||||
ASSERT_NE(targetDrawList, nullptr);
|
||||
|
||||
const bool flushed = backend.EndFrame(targetDrawList);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::EndFrame();
|
||||
|
||||
EXPECT_TRUE(flushed);
|
||||
EXPECT_EQ(backend.GetLastFlushedDrawListCount(), 2u);
|
||||
EXPECT_EQ(backend.GetLastFlushedCommandCount(), 3u);
|
||||
EXPECT_EQ(backend.GetPendingDrawListCount(), 0u);
|
||||
EXPECT_EQ(backend.GetPendingCommandCount(), 0u);
|
||||
}
|
||||
|
||||
TEST(NewEditorImGuiTransitionBackendTest, EndFrameAcceptsTextCommandsThatUseDefaultFontSize) {
|
||||
ImGuiContextScope contextScope;
|
||||
PrepareImGui(720.0f, 512.0f);
|
||||
|
||||
XCEngine::Editor::XCUIBackend::ImGuiTransitionBackend backend = {};
|
||||
XCEngine::UI::UIDrawList drawList("DefaultFontText");
|
||||
drawList.AddText(
|
||||
XCEngine::UI::UIPoint(24.0f, 30.0f),
|
||||
"fallback font text",
|
||||
XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
0.0f);
|
||||
|
||||
backend.BeginFrame();
|
||||
backend.Submit(drawList);
|
||||
|
||||
ImGui::NewFrame();
|
||||
ASSERT_TRUE(ImGui::Begin("NewEditorXCUIBackendDefaultFontText"));
|
||||
ImDrawList* targetDrawList = ImGui::GetWindowDrawList();
|
||||
ASSERT_NE(targetDrawList, nullptr);
|
||||
|
||||
const bool flushed = backend.EndFrame(targetDrawList);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::EndFrame();
|
||||
|
||||
EXPECT_TRUE(flushed);
|
||||
EXPECT_EQ(backend.GetLastFlushedDrawListCount(), 1u);
|
||||
EXPECT_EQ(backend.GetLastFlushedCommandCount(), 1u);
|
||||
EXPECT_EQ(backend.GetPendingDrawListCount(), 0u);
|
||||
EXPECT_EQ(backend.GetPendingCommandCount(), 0u);
|
||||
}
|
||||
|
||||
TEST(NewEditorImGuiTransitionBackendTest, EndFrameSkipsEmptyTextGeometryButClearsPendingState) {
|
||||
ImGuiContextScope contextScope;
|
||||
PrepareImGui(720.0f, 512.0f);
|
||||
|
||||
XCEngine::Editor::XCUIBackend::ImGuiTransitionBackend backend = {};
|
||||
XCEngine::UI::UIDrawList drawList("EmptyText");
|
||||
drawList.AddText(
|
||||
XCEngine::UI::UIPoint(24.0f, 30.0f),
|
||||
"",
|
||||
XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
14.0f);
|
||||
|
||||
backend.BeginFrame();
|
||||
backend.Submit(drawList);
|
||||
|
||||
ImGui::NewFrame();
|
||||
ASSERT_TRUE(ImGui::Begin("NewEditorXCUIBackendEmptyText"));
|
||||
ImGui::TextUnformatted("anchor");
|
||||
ImDrawList* targetDrawList = ImGui::GetWindowDrawList();
|
||||
ASSERT_NE(targetDrawList, nullptr);
|
||||
const int initialVertexCount = targetDrawList->VtxBuffer.Size;
|
||||
|
||||
const bool flushed = backend.EndFrame(targetDrawList);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::EndFrame();
|
||||
|
||||
EXPECT_TRUE(flushed);
|
||||
EXPECT_EQ(backend.GetLastFlushedDrawListCount(), 1u);
|
||||
EXPECT_EQ(backend.GetLastFlushedCommandCount(), 1u);
|
||||
EXPECT_EQ(targetDrawList->VtxBuffer.Size, initialVertexCount);
|
||||
EXPECT_EQ(backend.GetPendingDrawListCount(), 0u);
|
||||
EXPECT_EQ(backend.GetPendingCommandCount(), 0u);
|
||||
}
|
||||
|
||||
TEST(NewEditorImGuiTransitionBackendTest, EndFrameRestoresClipRectStackAfterUnbalancedPush) {
|
||||
ImGuiContextScope contextScope;
|
||||
PrepareImGui(800.0f, 600.0f);
|
||||
|
||||
XCEngine::Editor::XCUIBackend::ImGuiTransitionBackend backend = {};
|
||||
XCEngine::UI::UIDrawList drawList("ClipRecovery");
|
||||
drawList.PushClipRect(XCEngine::UI::UIRect(0.0f, 0.0f, 180.0f, 120.0f));
|
||||
drawList.AddFilledRect(
|
||||
XCEngine::UI::UIRect(16.0f, 18.0f, 52.0f, 30.0f),
|
||||
XCEngine::UI::UIColor(0.9f, 0.3f, 0.2f, 1.0f));
|
||||
|
||||
backend.BeginFrame();
|
||||
backend.Submit(drawList);
|
||||
|
||||
ImGui::NewFrame();
|
||||
ASSERT_TRUE(ImGui::Begin("NewEditorXCUIBackendClipRecovery"));
|
||||
ImDrawList* targetDrawList = ImGui::GetWindowDrawList();
|
||||
ASSERT_NE(targetDrawList, nullptr);
|
||||
const int initialClipDepth = targetDrawList->_ClipRectStack.Size;
|
||||
|
||||
const bool flushed = backend.EndFrame(targetDrawList);
|
||||
|
||||
EXPECT_TRUE(flushed);
|
||||
EXPECT_EQ(targetDrawList->_ClipRectStack.Size, initialClipDepth);
|
||||
EXPECT_EQ(backend.GetLastFlushedDrawListCount(), 1u);
|
||||
EXPECT_EQ(backend.GetLastFlushedCommandCount(), 2u);
|
||||
EXPECT_EQ(backend.GetPendingCommandCount(), 0u);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::EndFrame();
|
||||
}
|
||||
257
tests/NewEditor/test_xcui_asset_document_source.cpp
Normal file
257
tests/NewEditor/test_xcui_asset_document_source.cpp
Normal file
@@ -0,0 +1,257 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "XCUIBackend/XCUIAssetDocumentSource.h"
|
||||
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Core/Containers/String.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
using XCEngine::Containers::String;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIAssetDocumentSource;
|
||||
using XCEngine::Resources::ResourceManager;
|
||||
|
||||
String ToContainersString(const std::string& value) {
|
||||
return String(value.c_str());
|
||||
}
|
||||
|
||||
void WriteTextFile(const fs::path& path, const std::string& contents) {
|
||||
fs::create_directories(path.parent_path());
|
||||
std::ofstream output(path, std::ios::binary | std::ios::trunc);
|
||||
output << contents;
|
||||
}
|
||||
|
||||
std::string BuildMinimalViewDocument(const std::string& themeReference) {
|
||||
return
|
||||
"<View name=\"Test\" theme=\"" + themeReference + "\">\n"
|
||||
" <Column id=\"root\">\n"
|
||||
" <Text text=\"hello\" />\n"
|
||||
" </Column>\n"
|
||||
"</View>\n";
|
||||
}
|
||||
|
||||
std::string BuildMinimalThemeDocument() {
|
||||
return
|
||||
"<Theme name=\"TestTheme\">\n"
|
||||
" <Token name=\"color.text.primary\" type=\"color\" value=\"#FFFFFF\" />\n"
|
||||
"</Theme>\n";
|
||||
}
|
||||
|
||||
class ScopedCurrentPath {
|
||||
public:
|
||||
explicit ScopedCurrentPath(const fs::path& newPath) {
|
||||
m_originalPath = fs::current_path();
|
||||
fs::create_directories(newPath);
|
||||
fs::current_path(newPath);
|
||||
}
|
||||
|
||||
~ScopedCurrentPath() {
|
||||
if (!m_originalPath.empty()) {
|
||||
fs::current_path(m_originalPath);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
fs::path m_originalPath = {};
|
||||
};
|
||||
|
||||
class XCUIAssetDocumentSourceTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
m_originalResourceRoot = ResourceManager::Get().GetResourceRoot();
|
||||
ResourceManager::Get().SetResourceRoot(String());
|
||||
|
||||
m_tempRoot = fs::temp_directory_path() /
|
||||
fs::path("xcui_asset_document_source_tests");
|
||||
m_tempRoot /= fs::path(::testing::UnitTest::GetInstance()->current_test_info()->name());
|
||||
fs::remove_all(m_tempRoot);
|
||||
fs::create_directories(m_tempRoot);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
ResourceManager::Get().UnloadAll();
|
||||
ResourceManager::Get().SetResourceRoot(m_originalResourceRoot);
|
||||
|
||||
std::error_code ec;
|
||||
fs::remove_all(m_tempRoot, ec);
|
||||
}
|
||||
|
||||
fs::path CreateRepositorySubdir(const std::string& relativePath) const {
|
||||
const fs::path path = m_tempRoot / fs::path(relativePath);
|
||||
fs::create_directories(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
void WriteProjectDocuments(const XCUIAssetDocumentSource::PathSet& paths) const {
|
||||
WriteTextFile(
|
||||
m_tempRoot / fs::path(paths.view.primaryRelativePath),
|
||||
BuildMinimalViewDocument("Theme.xctheme"));
|
||||
WriteTextFile(
|
||||
m_tempRoot / fs::path(paths.theme.primaryRelativePath),
|
||||
BuildMinimalThemeDocument());
|
||||
}
|
||||
|
||||
void WriteLegacyDocuments(const XCUIAssetDocumentSource::PathSet& paths) const {
|
||||
WriteTextFile(
|
||||
m_tempRoot / fs::path(paths.view.legacyRelativePath),
|
||||
BuildMinimalViewDocument(
|
||||
fs::path(paths.theme.legacyRelativePath).filename().generic_string()));
|
||||
WriteTextFile(
|
||||
m_tempRoot / fs::path(paths.theme.legacyRelativePath),
|
||||
BuildMinimalThemeDocument());
|
||||
}
|
||||
|
||||
fs::path m_tempRoot = {};
|
||||
|
||||
private:
|
||||
String m_originalResourceRoot = {};
|
||||
};
|
||||
|
||||
TEST_F(XCUIAssetDocumentSourceTest, DemoAndLayoutLabPathSetsUseExpectedPaths) {
|
||||
const XCUIAssetDocumentSource::PathSet demoPaths =
|
||||
XCUIAssetDocumentSource::MakeDemoPathSet();
|
||||
EXPECT_EQ(demoPaths.setName, "Demo");
|
||||
EXPECT_EQ(demoPaths.view.primaryRelativePath, "Assets/XCUI/NewEditor/Demo/View.xcui");
|
||||
EXPECT_EQ(demoPaths.theme.primaryRelativePath, "Assets/XCUI/NewEditor/Demo/Theme.xctheme");
|
||||
EXPECT_EQ(demoPaths.view.legacyRelativePath, "new_editor/resources/xcui_demo_view.xcui");
|
||||
EXPECT_EQ(demoPaths.theme.legacyRelativePath, "new_editor/resources/xcui_demo_theme.xctheme");
|
||||
|
||||
const XCUIAssetDocumentSource::PathSet layoutLabPaths =
|
||||
XCUIAssetDocumentSource::MakeLayoutLabPathSet();
|
||||
EXPECT_EQ(layoutLabPaths.setName, "LayoutLab");
|
||||
EXPECT_EQ(layoutLabPaths.view.primaryRelativePath, "Assets/XCUI/NewEditor/LayoutLab/View.xcui");
|
||||
EXPECT_EQ(layoutLabPaths.theme.primaryRelativePath, "Assets/XCUI/NewEditor/LayoutLab/Theme.xctheme");
|
||||
EXPECT_EQ(layoutLabPaths.view.legacyRelativePath, "new_editor/resources/xcui_layout_lab_view.xcui");
|
||||
EXPECT_EQ(layoutLabPaths.theme.legacyRelativePath, "new_editor/resources/xcui_layout_lab_theme.xctheme");
|
||||
}
|
||||
|
||||
TEST_F(XCUIAssetDocumentSourceTest, MakePathSetSanitizesNamesAndBuildsLegacySnakeCase) {
|
||||
const XCUIAssetDocumentSource::PathSet paths =
|
||||
XCUIAssetDocumentSource::MakePathSet(" Layout Lab! Debug-42 ");
|
||||
|
||||
EXPECT_EQ(paths.setName, "LayoutLabDebug-42");
|
||||
EXPECT_EQ(
|
||||
paths.view.primaryRelativePath,
|
||||
"Assets/XCUI/NewEditor/LayoutLabDebug-42/View.xcui");
|
||||
EXPECT_EQ(
|
||||
paths.theme.legacyRelativePath,
|
||||
"new_editor/resources/xcui_layout_lab_debug_42_theme.xctheme");
|
||||
}
|
||||
|
||||
TEST_F(XCUIAssetDocumentSourceTest, CollectCandidatePathsPrefersProjectAssetsBeforeLegacyMirror) {
|
||||
const XCUIAssetDocumentSource::PathSet paths =
|
||||
XCUIAssetDocumentSource::MakePathSet("Worker1CandidateOrder");
|
||||
WriteProjectDocuments(paths);
|
||||
WriteLegacyDocuments(paths);
|
||||
|
||||
const std::vector<XCUIAssetDocumentSource::ResolutionCandidate> candidates =
|
||||
XCUIAssetDocumentSource::CollectCandidatePaths(paths.view, m_tempRoot, fs::path());
|
||||
|
||||
ASSERT_EQ(candidates.size(), 2u);
|
||||
EXPECT_EQ(candidates[0].origin, XCUIAssetDocumentSource::PathOrigin::ProjectAssets);
|
||||
EXPECT_EQ(candidates[0].resolvedPath, fs::path(m_tempRoot / paths.view.primaryRelativePath).lexically_normal());
|
||||
EXPECT_EQ(candidates[1].origin, XCUIAssetDocumentSource::PathOrigin::LegacyMirror);
|
||||
EXPECT_EQ(candidates[1].resolvedPath, fs::path(m_tempRoot / paths.view.legacyRelativePath).lexically_normal());
|
||||
}
|
||||
|
||||
TEST_F(XCUIAssetDocumentSourceTest, DiagnoseRepositoryRootReportsProjectAssetAncestor) {
|
||||
const XCUIAssetDocumentSource::PathSet paths =
|
||||
XCUIAssetDocumentSource::MakeDemoPathSet();
|
||||
WriteProjectDocuments(paths);
|
||||
|
||||
const fs::path searchRoot = CreateRepositorySubdir("tools/worker1/deep");
|
||||
const XCUIAssetDocumentSource::RepositoryDiscovery discovery =
|
||||
XCUIAssetDocumentSource::DiagnoseRepositoryRoot(paths, { searchRoot }, false);
|
||||
|
||||
EXPECT_EQ(discovery.repositoryRoot, m_tempRoot.lexically_normal());
|
||||
ASSERT_EQ(discovery.probes.size(), 1u);
|
||||
EXPECT_TRUE(discovery.probes[0].matched);
|
||||
EXPECT_EQ(discovery.probes[0].searchRoot, searchRoot.lexically_normal());
|
||||
EXPECT_EQ(discovery.probes[0].matchedRelativePath, paths.view.primaryRelativePath);
|
||||
EXPECT_NE(discovery.statusMessage.find(paths.view.primaryRelativePath), std::string::npos);
|
||||
}
|
||||
|
||||
TEST_F(XCUIAssetDocumentSourceTest, DiagnoseRepositoryRootReportsLegacyMirrorAncestor) {
|
||||
const XCUIAssetDocumentSource::PathSet paths =
|
||||
XCUIAssetDocumentSource::MakeLayoutLabPathSet();
|
||||
WriteLegacyDocuments(paths);
|
||||
|
||||
const fs::path searchRoot = CreateRepositorySubdir("sandbox/runtime/session");
|
||||
const XCUIAssetDocumentSource::RepositoryDiscovery discovery =
|
||||
XCUIAssetDocumentSource::DiagnoseRepositoryRoot(paths, { searchRoot }, false);
|
||||
|
||||
EXPECT_EQ(discovery.repositoryRoot, m_tempRoot.lexically_normal());
|
||||
ASSERT_EQ(discovery.probes.size(), 1u);
|
||||
EXPECT_TRUE(discovery.probes[0].matched);
|
||||
EXPECT_EQ(discovery.probes[0].matchedRelativePath, paths.view.legacyRelativePath);
|
||||
EXPECT_NE(discovery.statusMessage.find(paths.view.legacyRelativePath), std::string::npos);
|
||||
}
|
||||
|
||||
TEST_F(XCUIAssetDocumentSourceTest, ReloadUsesLegacyFallbackAndTracksSourceChanges) {
|
||||
const XCUIAssetDocumentSource::PathSet paths =
|
||||
XCUIAssetDocumentSource::MakePathSet("Worker1HotReloadRegression");
|
||||
WriteLegacyDocuments(paths);
|
||||
|
||||
const fs::path sandboxPath = CreateRepositorySubdir("runtime/worker1");
|
||||
ScopedCurrentPath scopedCurrentPath(sandboxPath);
|
||||
|
||||
XCUIAssetDocumentSource source(paths);
|
||||
ASSERT_TRUE(source.Reload());
|
||||
|
||||
const XCUIAssetDocumentSource::LoadState& initialState = source.GetState();
|
||||
EXPECT_TRUE(initialState.succeeded);
|
||||
EXPECT_TRUE(initialState.usedLegacyFallback);
|
||||
EXPECT_TRUE(initialState.changeTrackingReady);
|
||||
EXPECT_TRUE(initialState.missingTrackedSourcePaths.empty());
|
||||
EXPECT_EQ(initialState.repositoryRoot, m_tempRoot.lexically_normal());
|
||||
EXPECT_EQ(initialState.view.pathOrigin, XCUIAssetDocumentSource::PathOrigin::LegacyMirror);
|
||||
EXPECT_EQ(initialState.theme.pathOrigin, XCUIAssetDocumentSource::PathOrigin::LegacyMirror);
|
||||
EXPECT_FALSE(initialState.trackedSourcePaths.empty());
|
||||
EXPECT_NE(initialState.trackingStatusMessage.find("Tracking "), std::string::npos);
|
||||
EXPECT_FALSE(source.HasTrackedChanges());
|
||||
|
||||
const fs::path themePath = m_tempRoot / fs::path(paths.theme.legacyRelativePath);
|
||||
fs::last_write_time(
|
||||
themePath,
|
||||
fs::last_write_time(themePath) + std::chrono::seconds(2));
|
||||
|
||||
EXPECT_TRUE(source.HasTrackedChanges());
|
||||
ASSERT_TRUE(source.ReloadIfChanged());
|
||||
|
||||
const XCUIAssetDocumentSource::LoadState& reloadedState = source.GetState();
|
||||
EXPECT_EQ(
|
||||
reloadedState.view.backend,
|
||||
XCUIAssetDocumentSource::LoadBackend::CompilerFallback);
|
||||
EXPECT_EQ(
|
||||
reloadedState.theme.backend,
|
||||
XCUIAssetDocumentSource::LoadBackend::CompilerFallback);
|
||||
EXPECT_TRUE(reloadedState.changeTrackingReady);
|
||||
}
|
||||
|
||||
TEST_F(XCUIAssetDocumentSourceTest, ReloadFailureIncludesRepositoryDiscoveryDiagnostic) {
|
||||
const XCUIAssetDocumentSource::PathSet paths =
|
||||
XCUIAssetDocumentSource::MakePathSet("Worker1MissingDocuments");
|
||||
const fs::path sandboxPath = CreateRepositorySubdir("runtime/missing");
|
||||
ScopedCurrentPath scopedCurrentPath(sandboxPath);
|
||||
|
||||
XCUIAssetDocumentSource source(paths);
|
||||
EXPECT_FALSE(source.Reload());
|
||||
|
||||
const XCUIAssetDocumentSource::LoadState& state = source.GetState();
|
||||
EXPECT_FALSE(state.succeeded);
|
||||
EXPECT_TRUE(state.repositoryRoot.empty());
|
||||
EXPECT_NE(state.repositoryDiscovery.statusMessage.find("Repository root not found"), std::string::npos);
|
||||
EXPECT_NE(state.errorMessage.find(paths.view.primaryRelativePath), std::string::npos);
|
||||
EXPECT_NE(state.errorMessage.find("Repository root not found"), std::string::npos);
|
||||
EXPECT_TRUE(state.view.candidatePaths.empty());
|
||||
EXPECT_TRUE(state.view.attemptMessages.empty());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
336
tests/NewEditor/test_xcui_demo_runtime.cpp
Normal file
336
tests/NewEditor/test_xcui_demo_runtime.cpp
Normal file
@@ -0,0 +1,336 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "XCUIBackend/XCUIDemoRuntime.h"
|
||||
|
||||
#include <XCEngine/Input/InputTypes.h>
|
||||
#include <XCEngine/UI/Types.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
using XCEngine::UI::UIDrawCommand;
|
||||
using XCEngine::UI::UIDrawCommandType;
|
||||
using XCEngine::UI::UIInputEvent;
|
||||
using XCEngine::UI::UIInputEventType;
|
||||
|
||||
XCEngine::Editor::XCUIBackend::XCUIDemoInputState BuildInputState(
|
||||
float width = 720.0f,
|
||||
float height = 420.0f) {
|
||||
XCEngine::Editor::XCUIBackend::XCUIDemoInputState 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;
|
||||
}
|
||||
|
||||
UIInputEvent MakeCharacterEvent(std::uint32_t character) {
|
||||
UIInputEvent event = {};
|
||||
event.type = UIInputEventType::Character;
|
||||
event.character = character;
|
||||
return event;
|
||||
}
|
||||
|
||||
UIInputEvent MakeKeyDownEvent(XCEngine::Input::KeyCode keyCode, bool repeat = false) {
|
||||
UIInputEvent event = {};
|
||||
event.type = UIInputEventType::KeyDown;
|
||||
event.keyCode = static_cast<std::int32_t>(keyCode);
|
||||
event.repeat = repeat;
|
||||
return event;
|
||||
}
|
||||
|
||||
fs::path FindDemoResourcePath() {
|
||||
fs::path probe = fs::current_path();
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
const fs::path canonicalCandidate = probe / "Assets/XCUI/NewEditor/Demo/View.xcui";
|
||||
if (fs::exists(canonicalCandidate)) {
|
||||
return canonicalCandidate;
|
||||
}
|
||||
|
||||
const fs::path legacyCandidate = probe / "new_editor/resources/xcui_demo_view.xcui";
|
||||
if (fs::exists(legacyCandidate)) {
|
||||
return legacyCandidate;
|
||||
}
|
||||
|
||||
if (!probe.has_parent_path()) {
|
||||
break;
|
||||
}
|
||||
probe = probe.parent_path();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<const UIDrawCommand*> CollectTextCommands(const XCEngine::UI::UIDrawData& drawData) {
|
||||
std::vector<const UIDrawCommand*> 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;
|
||||
}
|
||||
|
||||
class FileTimestampRestoreScope {
|
||||
public:
|
||||
explicit FileTimestampRestoreScope(fs::path path)
|
||||
: m_path(std::move(path)) {
|
||||
if (!m_path.empty() && fs::exists(m_path)) {
|
||||
m_originalWriteTime = fs::last_write_time(m_path);
|
||||
std::ifstream input(m_path, std::ios::binary);
|
||||
std::ostringstream stream;
|
||||
stream << input.rdbuf();
|
||||
m_originalContents = stream.str();
|
||||
m_valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
~FileTimestampRestoreScope() {
|
||||
if (m_valid) {
|
||||
std::ofstream output(m_path, std::ios::binary | std::ios::trunc);
|
||||
output << m_originalContents;
|
||||
output.close();
|
||||
fs::last_write_time(m_path, m_originalWriteTime);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
fs::path m_path;
|
||||
fs::file_time_type m_originalWriteTime = {};
|
||||
std::string m_originalContents = {};
|
||||
bool m_valid = false;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(NewEditorXCUIDemoRuntimeTest, UpdateProvidesDeterministicFrameContainer) {
|
||||
XCEngine::Editor::XCUIBackend::XCUIDemoRuntime runtime;
|
||||
const bool reloadSucceeded = runtime.ReloadDocuments();
|
||||
|
||||
const auto& firstFrame = runtime.Update(BuildInputState());
|
||||
EXPECT_EQ(firstFrame.stats.documentsReady, reloadSucceeded);
|
||||
EXPECT_EQ(firstFrame.stats.drawListCount, firstFrame.drawData.GetDrawListCount());
|
||||
EXPECT_EQ(firstFrame.stats.commandCount, firstFrame.drawData.GetTotalCommandCount());
|
||||
|
||||
const auto& secondFrame = runtime.Update(BuildInputState());
|
||||
EXPECT_GE(secondFrame.stats.treeGeneration, firstFrame.stats.treeGeneration);
|
||||
EXPECT_EQ(secondFrame.stats.drawListCount, secondFrame.drawData.GetDrawListCount());
|
||||
EXPECT_EQ(secondFrame.stats.commandCount, secondFrame.drawData.GetTotalCommandCount());
|
||||
|
||||
if (secondFrame.stats.documentsReady) {
|
||||
EXPECT_GT(secondFrame.stats.elementCount, 0u);
|
||||
EXPECT_GT(secondFrame.stats.drawListCount, 0u);
|
||||
EXPECT_GT(secondFrame.stats.commandCount, 0u);
|
||||
} else {
|
||||
EXPECT_FALSE(secondFrame.stats.statusMessage.empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(NewEditorXCUIDemoRuntimeTest, RuntimeFrameEmitsTextCommandsWithResolvedFontSizes) {
|
||||
XCEngine::Editor::XCUIBackend::XCUIDemoRuntime runtime;
|
||||
ASSERT_TRUE(runtime.ReloadDocuments());
|
||||
|
||||
const auto& frame = runtime.Update(BuildInputState());
|
||||
ASSERT_TRUE(frame.stats.documentsReady);
|
||||
|
||||
const std::vector<const UIDrawCommand*> 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, "New XCUI Shell");
|
||||
ASSERT_NE(titleCommand, nullptr);
|
||||
EXPECT_FLOAT_EQ(titleCommand->fontSize, 18.0f);
|
||||
|
||||
const UIDrawCommand* metricValueCommand = FindTextCommand(frame.drawData, "Driven by runtime");
|
||||
ASSERT_NE(metricValueCommand, nullptr);
|
||||
EXPECT_FLOAT_EQ(metricValueCommand->fontSize, 18.0f);
|
||||
|
||||
const UIDrawCommand* buttonLabelCommand = FindTextCommand(frame.drawData, "Toggle Accent");
|
||||
ASSERT_NE(buttonLabelCommand, nullptr);
|
||||
EXPECT_FLOAT_EQ(buttonLabelCommand->fontSize, 14.0f);
|
||||
}
|
||||
|
||||
TEST(NewEditorXCUIDemoRuntimeTest, InputStateTransitionsAreAcceptedAndFrameStillBuilds) {
|
||||
XCEngine::Editor::XCUIBackend::XCUIDemoRuntime runtime;
|
||||
runtime.ReloadDocuments();
|
||||
|
||||
XCEngine::Editor::XCUIBackend::XCUIDemoInputState frameInput = BuildInputState();
|
||||
frameInput.pointerPressed = true;
|
||||
frameInput.pointerDown = true;
|
||||
const auto& pressedFrame = runtime.Update(frameInput);
|
||||
|
||||
frameInput.pointerPressed = false;
|
||||
frameInput.pointerReleased = true;
|
||||
frameInput.pointerDown = false;
|
||||
frameInput.shortcutPressed = true;
|
||||
const auto& releasedFrame = runtime.Update(frameInput);
|
||||
|
||||
EXPECT_GE(releasedFrame.stats.treeGeneration, pressedFrame.stats.treeGeneration);
|
||||
EXPECT_EQ(releasedFrame.stats.drawListCount, releasedFrame.drawData.GetDrawListCount());
|
||||
EXPECT_EQ(releasedFrame.stats.commandCount, releasedFrame.drawData.GetTotalCommandCount());
|
||||
|
||||
if (releasedFrame.stats.documentsReady) {
|
||||
EXPECT_GT(releasedFrame.stats.elementCount, 0u);
|
||||
EXPECT_GE(releasedFrame.stats.dirtyRootCount, 0u);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(NewEditorXCUIDemoRuntimeTest, PointerToggleUpdatesFocusStatusTextAndAccentState) {
|
||||
XCEngine::Editor::XCUIBackend::XCUIDemoRuntime runtime;
|
||||
ASSERT_TRUE(runtime.ReloadDocuments());
|
||||
|
||||
const auto& baselineFrame = runtime.Update(BuildInputState());
|
||||
ASSERT_TRUE(baselineFrame.stats.documentsReady);
|
||||
EXPECT_FALSE(baselineFrame.stats.accentEnabled);
|
||||
EXPECT_NE(
|
||||
FindTextCommand(baselineFrame.drawData, "Markup -> Layout -> Style -> DrawData"),
|
||||
nullptr);
|
||||
|
||||
XCEngine::UI::UIRect buttonRect = {};
|
||||
ASSERT_TRUE(runtime.TryGetElementRect("toggleAccent", buttonRect));
|
||||
|
||||
const XCEngine::UI::UIPoint buttonCenter(
|
||||
buttonRect.x + buttonRect.width * 0.5f,
|
||||
buttonRect.y + buttonRect.height * 0.5f);
|
||||
|
||||
XCEngine::Editor::XCUIBackend::XCUIDemoInputState pressedInput = BuildInputState();
|
||||
pressedInput.pointerPosition = buttonCenter;
|
||||
pressedInput.pointerPressed = true;
|
||||
pressedInput.pointerDown = true;
|
||||
const auto& pressedFrame = runtime.Update(pressedInput);
|
||||
ASSERT_TRUE(pressedFrame.stats.documentsReady);
|
||||
|
||||
XCEngine::Editor::XCUIBackend::XCUIDemoInputState releasedInput = BuildInputState();
|
||||
releasedInput.pointerPosition = buttonCenter;
|
||||
releasedInput.pointerReleased = true;
|
||||
const auto& toggledFrame = runtime.Update(releasedInput);
|
||||
|
||||
ASSERT_TRUE(toggledFrame.stats.documentsReady);
|
||||
EXPECT_TRUE(toggledFrame.stats.accentEnabled);
|
||||
EXPECT_EQ(toggledFrame.stats.lastCommandId, "demo.toggleAccent");
|
||||
EXPECT_EQ(toggledFrame.stats.focusedElementId, "toggleAccent");
|
||||
|
||||
const UIDrawCommand* focusStatusCommand = FindTextCommand(
|
||||
toggledFrame.drawData,
|
||||
"Focus: toggleAccent");
|
||||
ASSERT_NE(focusStatusCommand, nullptr);
|
||||
EXPECT_FLOAT_EQ(focusStatusCommand->fontSize, 14.0f);
|
||||
}
|
||||
|
||||
TEST(NewEditorXCUIDemoRuntimeTest, UpdateAutoReloadsWhenSourceTimestampChanges) {
|
||||
const fs::path viewPath = FindDemoResourcePath();
|
||||
ASSERT_FALSE(viewPath.empty());
|
||||
ASSERT_TRUE(fs::exists(viewPath));
|
||||
|
||||
FileTimestampRestoreScope restoreScope(viewPath);
|
||||
|
||||
XCEngine::Editor::XCUIBackend::XCUIDemoRuntime runtime;
|
||||
ASSERT_TRUE(runtime.ReloadDocuments());
|
||||
|
||||
const auto& baselineFrame = runtime.Update(BuildInputState());
|
||||
ASSERT_TRUE(baselineFrame.stats.documentsReady);
|
||||
XCEngine::UI::UIRect probeRect = {};
|
||||
EXPECT_FALSE(runtime.TryGetElementRect("autoReloadProbe", probeRect));
|
||||
|
||||
std::ifstream input(viewPath, std::ios::binary);
|
||||
std::ostringstream stream;
|
||||
stream << input.rdbuf();
|
||||
const std::string originalContents = stream.str();
|
||||
input.close();
|
||||
|
||||
const std::string marker = "</Column>\n</View>";
|
||||
const std::size_t insertPosition = originalContents.rfind(marker);
|
||||
ASSERT_NE(insertPosition, std::string::npos);
|
||||
|
||||
const std::string injectedNode =
|
||||
" <Text id=\"autoReloadProbe\" text=\"Auto Reload Probe\" style=\"Meta\" />\n";
|
||||
std::string modifiedContents = originalContents;
|
||||
modifiedContents.insert(insertPosition, injectedNode);
|
||||
|
||||
std::ofstream output(viewPath, std::ios::binary | std::ios::trunc);
|
||||
output << modifiedContents;
|
||||
output.close();
|
||||
|
||||
const fs::file_time_type originalWriteTime = fs::last_write_time(viewPath);
|
||||
fs::last_write_time(viewPath, originalWriteTime + std::chrono::seconds(2));
|
||||
|
||||
const auto& reloadedFrame = runtime.Update(BuildInputState());
|
||||
EXPECT_TRUE(reloadedFrame.stats.documentsReady);
|
||||
EXPECT_GT(reloadedFrame.stats.elementCount, 0u);
|
||||
EXPECT_GT(reloadedFrame.stats.commandCount, 0u);
|
||||
EXPECT_TRUE(runtime.TryGetElementRect("autoReloadProbe", probeRect));
|
||||
}
|
||||
|
||||
TEST(NewEditorXCUIDemoRuntimeTest, TextFieldAcceptsUtf8CharactersAndBackspace) {
|
||||
XCEngine::Editor::XCUIBackend::XCUIDemoRuntime runtime;
|
||||
ASSERT_TRUE(runtime.ReloadDocuments());
|
||||
|
||||
const auto& baselineFrame = runtime.Update(BuildInputState());
|
||||
ASSERT_TRUE(baselineFrame.stats.documentsReady);
|
||||
|
||||
XCEngine::UI::UIRect promptRect = {};
|
||||
ASSERT_TRUE(runtime.TryGetElementRect("agentPrompt", promptRect));
|
||||
|
||||
const XCEngine::UI::UIPoint promptCenter(
|
||||
promptRect.x + promptRect.width * 0.5f,
|
||||
promptRect.y + promptRect.height * 0.5f);
|
||||
|
||||
XCEngine::Editor::XCUIBackend::XCUIDemoInputState pressedInput = BuildInputState();
|
||||
pressedInput.pointerPosition = promptCenter;
|
||||
pressedInput.pointerPressed = true;
|
||||
pressedInput.pointerDown = true;
|
||||
runtime.Update(pressedInput);
|
||||
|
||||
XCEngine::Editor::XCUIBackend::XCUIDemoInputState releasedInput = BuildInputState();
|
||||
releasedInput.pointerPosition = promptCenter;
|
||||
releasedInput.pointerReleased = true;
|
||||
const auto& focusedFrame = runtime.Update(releasedInput);
|
||||
ASSERT_TRUE(focusedFrame.stats.documentsReady);
|
||||
EXPECT_EQ(focusedFrame.stats.focusedElementId, "agentPrompt");
|
||||
|
||||
XCEngine::Editor::XCUIBackend::XCUIDemoInputState textInput = BuildInputState();
|
||||
textInput.events.push_back(MakeCharacterEvent('A'));
|
||||
textInput.events.push_back(MakeCharacterEvent('I'));
|
||||
textInput.events.push_back(MakeCharacterEvent(0x4F60u));
|
||||
const auto& typedFrame = runtime.Update(textInput);
|
||||
|
||||
ASSERT_TRUE(typedFrame.stats.documentsReady);
|
||||
EXPECT_EQ(typedFrame.stats.focusedElementId, "agentPrompt");
|
||||
EXPECT_NE(FindTextCommand(typedFrame.drawData, "AI你"), nullptr);
|
||||
EXPECT_EQ(typedFrame.stats.lastCommandId, "demo.text.edit.agentPrompt");
|
||||
|
||||
XCEngine::Editor::XCUIBackend::XCUIDemoInputState backspaceInput = BuildInputState();
|
||||
backspaceInput.events.push_back(MakeKeyDownEvent(XCEngine::Input::KeyCode::Backspace));
|
||||
const auto& backspacedFrame = runtime.Update(backspaceInput);
|
||||
|
||||
ASSERT_TRUE(backspacedFrame.stats.documentsReady);
|
||||
EXPECT_NE(FindTextCommand(backspacedFrame.drawData, "AI"), nullptr);
|
||||
EXPECT_EQ(backspacedFrame.stats.focusedElementId, "agentPrompt");
|
||||
}
|
||||
523
tests/NewEditor/test_xcui_hosted_preview_presenter.cpp
Normal file
523
tests/NewEditor/test_xcui_hosted_preview_presenter.cpp
Normal file
@@ -0,0 +1,523 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "XCUIBackend/XCUIHostedPreviewPresenter.h"
|
||||
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::Editor::XCUIBackend::CreateImGuiXCUIHostedPreviewPresenter;
|
||||
using XCEngine::Editor::XCUIBackend::CreateQueuedNativeXCUIHostedPreviewPresenter;
|
||||
using XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewFrame;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewDrainStats;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewQueue;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewQueuedFrame;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceDescriptor;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceImage;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceRegistry;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewStats;
|
||||
|
||||
class ImGuiContextScope {
|
||||
public:
|
||||
ImGuiContextScope() {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGui::StyleColorsDark();
|
||||
}
|
||||
|
||||
~ImGuiContextScope() {
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
};
|
||||
|
||||
void PrepareImGui(float width = 1024.0f, float height = 768.0f) {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.DisplaySize = ImVec2(width, height);
|
||||
io.DeltaTime = 1.0f / 60.0f;
|
||||
unsigned char* fontPixels = nullptr;
|
||||
int fontWidth = 0;
|
||||
int fontHeight = 0;
|
||||
io.Fonts->GetTexDataAsRGBA32(&fontPixels, &fontWidth, &fontHeight);
|
||||
io.Fonts->SetTexID(static_cast<ImTextureID>(1));
|
||||
}
|
||||
|
||||
TEST(XCUIHostedPreviewPresenterTest, PresentReturnsFalseAndClearsStatsWhenFrameHasNoDrawData) {
|
||||
ImGuiContextScope contextScope;
|
||||
PrepareImGui();
|
||||
|
||||
std::unique_ptr<IXCUIHostedPreviewPresenter> presenter = CreateImGuiXCUIHostedPreviewPresenter();
|
||||
ASSERT_NE(presenter, nullptr);
|
||||
|
||||
XCUIHostedPreviewFrame frame = {};
|
||||
const bool presented = presenter->Present(frame);
|
||||
const XCUIHostedPreviewStats& stats = presenter->GetLastStats();
|
||||
|
||||
EXPECT_FALSE(presented);
|
||||
EXPECT_FALSE(stats.presented);
|
||||
EXPECT_EQ(stats.submittedDrawListCount, 0u);
|
||||
EXPECT_EQ(stats.submittedCommandCount, 0u);
|
||||
EXPECT_EQ(stats.flushedDrawListCount, 0u);
|
||||
EXPECT_EQ(stats.flushedCommandCount, 0u);
|
||||
}
|
||||
|
||||
TEST(XCUIHostedPreviewPresenterTest, PresentFlushesDrawDataIntoProvidedImGuiDrawList) {
|
||||
ImGuiContextScope contextScope;
|
||||
PrepareImGui(800.0f, 600.0f);
|
||||
|
||||
std::unique_ptr<IXCUIHostedPreviewPresenter> presenter = CreateImGuiXCUIHostedPreviewPresenter();
|
||||
ASSERT_NE(presenter, nullptr);
|
||||
|
||||
XCEngine::UI::UIDrawData drawData = {};
|
||||
XCEngine::UI::UIDrawList& drawList = drawData.EmplaceDrawList("HostedPreview");
|
||||
drawList.AddFilledRect(
|
||||
XCEngine::UI::UIRect(12.0f, 14.0f, 48.0f, 30.0f),
|
||||
XCEngine::UI::UIColor(0.25f, 0.5f, 0.8f, 1.0f));
|
||||
drawList.AddText(
|
||||
XCEngine::UI::UIPoint(18.0f, 24.0f),
|
||||
"xcui",
|
||||
XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
14.0f);
|
||||
|
||||
ImGui::NewFrame();
|
||||
ASSERT_TRUE(ImGui::Begin("HostedPreviewPresenterWindow"));
|
||||
ImDrawList* targetDrawList = ImGui::GetWindowDrawList();
|
||||
ASSERT_NE(targetDrawList, nullptr);
|
||||
|
||||
XCUIHostedPreviewFrame frame = {};
|
||||
frame.drawData = &drawData;
|
||||
frame.targetDrawList = targetDrawList;
|
||||
|
||||
const bool presented = presenter->Present(frame);
|
||||
const XCUIHostedPreviewStats& stats = presenter->GetLastStats();
|
||||
|
||||
ImGui::End();
|
||||
ImGui::EndFrame();
|
||||
|
||||
EXPECT_TRUE(presented);
|
||||
EXPECT_TRUE(stats.presented);
|
||||
EXPECT_EQ(stats.submittedDrawListCount, 1u);
|
||||
EXPECT_EQ(stats.submittedCommandCount, 2u);
|
||||
EXPECT_EQ(stats.flushedDrawListCount, 1u);
|
||||
EXPECT_EQ(stats.flushedCommandCount, 2u);
|
||||
EXPECT_GT(targetDrawList->VtxBuffer.Size, 0);
|
||||
EXPECT_GT(targetDrawList->CmdBuffer.Size, 0);
|
||||
}
|
||||
|
||||
TEST(XCUIHostedPreviewPresenterTest, QueuedNativePresenterCopiesFrameIntoQueue) {
|
||||
XCUIHostedPreviewQueue queue = {};
|
||||
XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {};
|
||||
queue.BeginFrame();
|
||||
|
||||
std::unique_ptr<IXCUIHostedPreviewPresenter> presenter =
|
||||
CreateQueuedNativeXCUIHostedPreviewPresenter(queue, surfaceRegistry);
|
||||
ASSERT_NE(presenter, nullptr);
|
||||
EXPECT_TRUE(presenter->IsNativeQueued());
|
||||
|
||||
XCEngine::UI::UIDrawData drawData = {};
|
||||
XCEngine::UI::UIDrawList& drawList = drawData.EmplaceDrawList("HostedPreviewNative");
|
||||
drawList.AddFilledRect(
|
||||
XCEngine::UI::UIRect(20.0f, 24.0f, 56.0f, 32.0f),
|
||||
XCEngine::UI::UIColor(0.2f, 0.35f, 0.9f, 1.0f));
|
||||
drawList.AddText(
|
||||
XCEngine::UI::UIPoint(28.0f, 36.0f),
|
||||
"native",
|
||||
XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
16.0f);
|
||||
|
||||
XCUIHostedPreviewFrame frame = {};
|
||||
frame.drawData = &drawData;
|
||||
frame.debugName = "Hosted Preview Native Queue";
|
||||
frame.debugSource = "tests.hosted_preview.native_queue";
|
||||
frame.canvasRect = XCEngine::UI::UIRect(32.0f, 48.0f, 320.0f, 180.0f);
|
||||
frame.logicalSize = XCEngine::UI::UISize(640.0f, 360.0f);
|
||||
|
||||
EXPECT_TRUE(presenter->Present(frame));
|
||||
|
||||
const XCUIHostedPreviewStats& stats = presenter->GetLastStats();
|
||||
EXPECT_TRUE(stats.presented);
|
||||
EXPECT_TRUE(stats.queuedToNativePass);
|
||||
EXPECT_EQ(stats.submittedDrawListCount, 1u);
|
||||
EXPECT_EQ(stats.submittedCommandCount, 2u);
|
||||
EXPECT_EQ(stats.flushedDrawListCount, 0u);
|
||||
EXPECT_EQ(stats.flushedCommandCount, 0u);
|
||||
|
||||
drawData.Clear();
|
||||
|
||||
const auto& queuedFrames = queue.GetQueuedFrames();
|
||||
ASSERT_EQ(queuedFrames.size(), 1u);
|
||||
EXPECT_EQ(queuedFrames[0].debugName, "Hosted Preview Native Queue");
|
||||
EXPECT_EQ(queuedFrames[0].debugSource, "tests.hosted_preview.native_queue");
|
||||
EXPECT_FLOAT_EQ(queuedFrames[0].canvasRect.x, 32.0f);
|
||||
EXPECT_FLOAT_EQ(queuedFrames[0].canvasRect.y, 48.0f);
|
||||
EXPECT_FLOAT_EQ(queuedFrames[0].canvasRect.width, 320.0f);
|
||||
EXPECT_FLOAT_EQ(queuedFrames[0].canvasRect.height, 180.0f);
|
||||
EXPECT_FLOAT_EQ(queuedFrames[0].logicalSize.width, 640.0f);
|
||||
EXPECT_FLOAT_EQ(queuedFrames[0].logicalSize.height, 360.0f);
|
||||
EXPECT_EQ(queuedFrames[0].drawData.GetDrawListCount(), 1u);
|
||||
EXPECT_EQ(queuedFrames[0].drawData.GetTotalCommandCount(), 2u);
|
||||
}
|
||||
|
||||
TEST(XCUIHostedPreviewPresenterTest, QueuedNativePresenterFallsBackLogicalSizeToCanvasRectAndDelegatesSurfaceQueries) {
|
||||
XCUIHostedPreviewQueue queue = {};
|
||||
XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {};
|
||||
queue.BeginFrame();
|
||||
surfaceRegistry.BeginFrame();
|
||||
|
||||
std::unique_ptr<IXCUIHostedPreviewPresenter> presenter =
|
||||
CreateQueuedNativeXCUIHostedPreviewPresenter(queue, surfaceRegistry);
|
||||
ASSERT_NE(presenter, nullptr);
|
||||
|
||||
XCEngine::UI::UIDrawData drawData = {};
|
||||
XCEngine::UI::UIDrawList& drawList = drawData.EmplaceDrawList("HostedPreviewFallback");
|
||||
drawList.AddFilledRect(
|
||||
XCEngine::UI::UIRect(6.0f, 10.0f, 40.0f, 24.0f),
|
||||
XCEngine::UI::UIColor(0.85f, 0.35f, 0.2f, 1.0f));
|
||||
|
||||
XCUIHostedPreviewFrame frame = {};
|
||||
frame.drawData = &drawData;
|
||||
frame.debugName = "XCUI Demo";
|
||||
frame.debugSource = "tests.hosted_preview.logical_size_fallback";
|
||||
frame.canvasRect = XCEngine::UI::UIRect(18.0f, 22.0f, 320.0f, 180.0f);
|
||||
|
||||
ASSERT_TRUE(presenter->Present(frame));
|
||||
ASSERT_EQ(queue.GetQueuedFrames().size(), 1u);
|
||||
|
||||
const XCUIHostedPreviewQueuedFrame& queuedFrame = queue.GetQueuedFrames().front();
|
||||
EXPECT_FLOAT_EQ(queuedFrame.logicalSize.width, 320.0f);
|
||||
EXPECT_FLOAT_EQ(queuedFrame.logicalSize.height, 180.0f);
|
||||
|
||||
XCUIHostedPreviewSurfaceDescriptor descriptor = {};
|
||||
EXPECT_FALSE(presenter->TryGetSurfaceDescriptor("XCUI Demo", descriptor));
|
||||
EXPECT_TRUE(descriptor.debugName.empty());
|
||||
|
||||
surfaceRegistry.RecordQueuedFrame(queuedFrame, 0u);
|
||||
|
||||
ASSERT_TRUE(presenter->TryGetSurfaceDescriptor("XCUI Demo", descriptor));
|
||||
EXPECT_EQ(descriptor.debugName, "XCUI Demo");
|
||||
EXPECT_EQ(descriptor.debugSource, "tests.hosted_preview.logical_size_fallback");
|
||||
EXPECT_FLOAT_EQ(descriptor.canvasRect.x, 18.0f);
|
||||
EXPECT_FLOAT_EQ(descriptor.canvasRect.y, 22.0f);
|
||||
EXPECT_FLOAT_EQ(descriptor.canvasRect.width, 320.0f);
|
||||
EXPECT_FLOAT_EQ(descriptor.canvasRect.height, 180.0f);
|
||||
EXPECT_FLOAT_EQ(descriptor.logicalSize.width, 320.0f);
|
||||
EXPECT_FLOAT_EQ(descriptor.logicalSize.height, 180.0f);
|
||||
EXPECT_EQ(descriptor.submittedDrawListCount, 1u);
|
||||
EXPECT_EQ(descriptor.submittedCommandCount, 1u);
|
||||
EXPECT_TRUE(descriptor.queuedThisFrame);
|
||||
|
||||
XCUIHostedPreviewSurfaceImage image = {};
|
||||
EXPECT_FALSE(presenter->TryGetSurfaceImage("XCUI Demo", image));
|
||||
EXPECT_FALSE(image.IsValid());
|
||||
|
||||
surfaceRegistry.UpdateSurface(
|
||||
"XCUI Demo",
|
||||
static_cast<ImTextureID>(static_cast<intptr_t>(11)),
|
||||
640u,
|
||||
360u,
|
||||
XCEngine::UI::UIRect(0.0f, 0.0f, 320.0f, 180.0f));
|
||||
|
||||
ASSERT_TRUE(presenter->TryGetSurfaceImage("XCUI Demo", image));
|
||||
EXPECT_TRUE(image.IsValid());
|
||||
EXPECT_EQ(image.textureId, static_cast<ImTextureID>(static_cast<intptr_t>(11)));
|
||||
EXPECT_FLOAT_EQ(image.uvMin.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(image.uvMin.y, 0.0f);
|
||||
EXPECT_FLOAT_EQ(image.uvMax.x, 0.5f);
|
||||
EXPECT_FLOAT_EQ(image.uvMax.y, 0.5f);
|
||||
}
|
||||
|
||||
TEST(XCUIHostedPreviewPresenterTest, QueuedNativePresenterRejectsMissingDrawDataAndLeavesQueueUntouched) {
|
||||
XCUIHostedPreviewQueue queue = {};
|
||||
XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {};
|
||||
queue.BeginFrame();
|
||||
|
||||
std::unique_ptr<IXCUIHostedPreviewPresenter> presenter =
|
||||
CreateQueuedNativeXCUIHostedPreviewPresenter(queue, surfaceRegistry);
|
||||
ASSERT_NE(presenter, nullptr);
|
||||
|
||||
XCUIHostedPreviewFrame frame = {};
|
||||
frame.debugName = "Missing DrawData";
|
||||
frame.targetDrawList = reinterpret_cast<ImDrawList*>(1);
|
||||
|
||||
EXPECT_FALSE(presenter->Present(frame));
|
||||
|
||||
const XCUIHostedPreviewStats& stats = presenter->GetLastStats();
|
||||
EXPECT_FALSE(stats.presented);
|
||||
EXPECT_FALSE(stats.queuedToNativePass);
|
||||
EXPECT_EQ(stats.submittedDrawListCount, 0u);
|
||||
EXPECT_EQ(stats.submittedCommandCount, 0u);
|
||||
EXPECT_EQ(stats.flushedDrawListCount, 0u);
|
||||
EXPECT_EQ(stats.flushedCommandCount, 0u);
|
||||
EXPECT_TRUE(queue.GetQueuedFrames().empty());
|
||||
}
|
||||
|
||||
TEST(XCUIHostedPreviewPresenterTest, HostedPreviewQueuePreservesSubmissionOrderAndPayloadMetadata) {
|
||||
XCUIHostedPreviewQueue queue = {};
|
||||
queue.BeginFrame();
|
||||
|
||||
XCEngine::UI::UIDrawData firstDrawData = {};
|
||||
XCEngine::UI::UIDrawList& firstDrawList = firstDrawData.EmplaceDrawList("FirstPreview");
|
||||
firstDrawList.AddFilledRect(
|
||||
XCEngine::UI::UIRect(0.0f, 0.0f, 20.0f, 10.0f),
|
||||
XCEngine::UI::UIColor(1.0f, 0.2f, 0.2f, 1.0f));
|
||||
|
||||
XCEngine::UI::UIDrawData secondDrawData = {};
|
||||
XCEngine::UI::UIDrawList& secondDrawList = secondDrawData.EmplaceDrawList("SecondPreview");
|
||||
secondDrawList.AddFilledRect(
|
||||
XCEngine::UI::UIRect(10.0f, 8.0f, 24.0f, 14.0f),
|
||||
XCEngine::UI::UIColor(0.2f, 0.7f, 1.0f, 1.0f));
|
||||
secondDrawList.AddText(
|
||||
XCEngine::UI::UIPoint(12.0f, 12.0f),
|
||||
"queued",
|
||||
XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
13.0f);
|
||||
|
||||
XCUIHostedPreviewStats firstStats = {};
|
||||
XCUIHostedPreviewFrame firstFrame = {};
|
||||
firstFrame.drawData = &firstDrawData;
|
||||
firstFrame.debugName = "First Native Preview";
|
||||
ASSERT_TRUE(queue.Submit(firstFrame, &firstStats));
|
||||
|
||||
XCUIHostedPreviewStats secondStats = {};
|
||||
XCUIHostedPreviewFrame secondFrame = {};
|
||||
secondFrame.drawData = &secondDrawData;
|
||||
ASSERT_TRUE(queue.Submit(secondFrame, &secondStats));
|
||||
|
||||
firstDrawData.Clear();
|
||||
secondDrawData.Clear();
|
||||
|
||||
const auto& queuedFrames = queue.GetQueuedFrames();
|
||||
ASSERT_EQ(queuedFrames.size(), 2u);
|
||||
|
||||
EXPECT_EQ(firstStats.submittedDrawListCount, 1u);
|
||||
EXPECT_EQ(firstStats.submittedCommandCount, 1u);
|
||||
EXPECT_TRUE(firstStats.queuedToNativePass);
|
||||
EXPECT_EQ(secondStats.submittedDrawListCount, 1u);
|
||||
EXPECT_EQ(secondStats.submittedCommandCount, 2u);
|
||||
EXPECT_TRUE(secondStats.queuedToNativePass);
|
||||
|
||||
EXPECT_EQ(queuedFrames[0].debugName, "First Native Preview");
|
||||
EXPECT_EQ(queuedFrames[0].drawData.GetDrawListCount(), 1u);
|
||||
EXPECT_EQ(queuedFrames[0].drawData.GetTotalCommandCount(), 1u);
|
||||
EXPECT_EQ(queuedFrames[1].debugName, "");
|
||||
EXPECT_EQ(queuedFrames[1].drawData.GetDrawListCount(), 1u);
|
||||
EXPECT_EQ(queuedFrames[1].drawData.GetTotalCommandCount(), 2u);
|
||||
}
|
||||
|
||||
TEST(XCUIHostedPreviewPresenterTest, SurfaceRegistryIgnoresUnnamedQueuedFramesAndKeepsDescriptorListStable) {
|
||||
XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {};
|
||||
|
||||
XCUIHostedPreviewQueuedFrame unnamedFrame = {};
|
||||
unnamedFrame.debugSource = "tests.hosted_preview.unnamed";
|
||||
unnamedFrame.canvasRect = XCEngine::UI::UIRect(10.0f, 12.0f, 120.0f, 80.0f);
|
||||
unnamedFrame.logicalSize = XCEngine::UI::UISize(120.0f, 80.0f);
|
||||
unnamedFrame.drawData.EmplaceDrawList("Unnamed").AddFilledRect(
|
||||
XCEngine::UI::UIRect(0.0f, 0.0f, 16.0f, 16.0f),
|
||||
XCEngine::UI::UIColor(0.4f, 0.7f, 0.9f, 1.0f));
|
||||
|
||||
surfaceRegistry.RecordQueuedFrame(unnamedFrame, 5u);
|
||||
EXPECT_TRUE(surfaceRegistry.GetDescriptors().empty());
|
||||
|
||||
surfaceRegistry.UpdateSurface(
|
||||
"XCUI Demo",
|
||||
static_cast<ImTextureID>(static_cast<intptr_t>(13)),
|
||||
800u,
|
||||
600u,
|
||||
XCEngine::UI::UIRect(0.0f, 0.0f, 400.0f, 300.0f));
|
||||
ASSERT_EQ(surfaceRegistry.GetDescriptors().size(), 1u);
|
||||
|
||||
surfaceRegistry.RecordQueuedFrame(unnamedFrame, 9u);
|
||||
ASSERT_EQ(surfaceRegistry.GetDescriptors().size(), 1u);
|
||||
EXPECT_EQ(surfaceRegistry.GetDescriptors().front().debugName, "XCUI Demo");
|
||||
EXPECT_TRUE(surfaceRegistry.GetDescriptors().front().image.IsValid());
|
||||
}
|
||||
|
||||
TEST(XCUIHostedPreviewPresenterTest, SurfaceRegistryExposesImageUvForRenderedCanvasRect) {
|
||||
XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {};
|
||||
XCUIHostedPreviewSurfaceImage image = {};
|
||||
|
||||
surfaceRegistry.UpdateSurface(
|
||||
"XCUI Demo",
|
||||
static_cast<ImTextureID>(static_cast<intptr_t>(7)),
|
||||
1024u,
|
||||
768u,
|
||||
XCEngine::UI::UIRect(128.0f, 96.0f, 512.0f, 384.0f));
|
||||
|
||||
ASSERT_TRUE(surfaceRegistry.TryGetSurfaceImage("XCUI Demo", image));
|
||||
EXPECT_TRUE(image.IsValid());
|
||||
EXPECT_EQ(image.textureId, static_cast<ImTextureID>(static_cast<intptr_t>(7)));
|
||||
EXPECT_EQ(image.surfaceWidth, 1024u);
|
||||
EXPECT_EQ(image.surfaceHeight, 768u);
|
||||
EXPECT_FLOAT_EQ(image.uvMin.x, 0.125f);
|
||||
EXPECT_FLOAT_EQ(image.uvMin.y, 0.125f);
|
||||
EXPECT_FLOAT_EQ(image.uvMax.x, 0.625f);
|
||||
EXPECT_FLOAT_EQ(image.uvMax.y, 0.625f);
|
||||
}
|
||||
|
||||
TEST(XCUIHostedPreviewPresenterTest, SurfaceRegistryTracksQueuedFrameMetadataAlongsideLatestSurfaceImage) {
|
||||
XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {};
|
||||
surfaceRegistry.BeginFrame();
|
||||
|
||||
XCEngine::UI::UIDrawData drawData = {};
|
||||
XCEngine::UI::UIDrawList& drawList = drawData.EmplaceDrawList("RegistryPreview");
|
||||
drawList.AddFilledRect(
|
||||
XCEngine::UI::UIRect(8.0f, 6.0f, 30.0f, 18.0f),
|
||||
XCEngine::UI::UIColor(0.3f, 0.7f, 0.9f, 1.0f));
|
||||
drawList.AddText(
|
||||
XCEngine::UI::UIPoint(16.0f, 12.0f),
|
||||
"meta",
|
||||
XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
12.0f);
|
||||
|
||||
XCUIHostedPreviewQueuedFrame queuedFrame = {};
|
||||
queuedFrame.debugName = "XCUI Demo";
|
||||
queuedFrame.debugSource = "tests.hosted_preview.registry";
|
||||
queuedFrame.canvasRect = XCEngine::UI::UIRect(24.0f, 32.0f, 300.0f, 160.0f);
|
||||
queuedFrame.logicalSize = XCEngine::UI::UISize(600.0f, 320.0f);
|
||||
queuedFrame.drawData = drawData;
|
||||
|
||||
surfaceRegistry.RecordQueuedFrame(queuedFrame, 2u);
|
||||
|
||||
XCUIHostedPreviewSurfaceDescriptor descriptor = {};
|
||||
ASSERT_TRUE(surfaceRegistry.TryGetSurfaceDescriptor("XCUI Demo", descriptor));
|
||||
EXPECT_EQ(descriptor.debugName, "XCUI Demo");
|
||||
EXPECT_EQ(descriptor.debugSource, "tests.hosted_preview.registry");
|
||||
EXPECT_FLOAT_EQ(descriptor.canvasRect.x, 24.0f);
|
||||
EXPECT_FLOAT_EQ(descriptor.canvasRect.y, 32.0f);
|
||||
EXPECT_FLOAT_EQ(descriptor.canvasRect.width, 300.0f);
|
||||
EXPECT_FLOAT_EQ(descriptor.canvasRect.height, 160.0f);
|
||||
EXPECT_FLOAT_EQ(descriptor.logicalSize.width, 600.0f);
|
||||
EXPECT_FLOAT_EQ(descriptor.logicalSize.height, 320.0f);
|
||||
EXPECT_EQ(descriptor.queuedFrameIndex, 2u);
|
||||
EXPECT_EQ(descriptor.submittedDrawListCount, 1u);
|
||||
EXPECT_EQ(descriptor.submittedCommandCount, 2u);
|
||||
EXPECT_TRUE(descriptor.queuedThisFrame);
|
||||
EXPECT_FALSE(descriptor.image.IsValid());
|
||||
|
||||
surfaceRegistry.UpdateSurface(
|
||||
"XCUI Demo",
|
||||
static_cast<ImTextureID>(static_cast<intptr_t>(9)),
|
||||
1024u,
|
||||
512u,
|
||||
XCEngine::UI::UIRect(128.0f, 64.0f, 320.0f, 160.0f));
|
||||
|
||||
ASSERT_TRUE(surfaceRegistry.TryGetSurfaceDescriptor("XCUI Demo", descriptor));
|
||||
EXPECT_TRUE(descriptor.queuedThisFrame);
|
||||
EXPECT_TRUE(descriptor.image.IsValid());
|
||||
EXPECT_EQ(descriptor.image.textureId, static_cast<ImTextureID>(static_cast<intptr_t>(9)));
|
||||
EXPECT_FLOAT_EQ(descriptor.image.uvMin.x, 0.125f);
|
||||
EXPECT_FLOAT_EQ(descriptor.image.uvMin.y, 0.125f);
|
||||
EXPECT_FLOAT_EQ(descriptor.image.uvMax.x, 0.4375f);
|
||||
EXPECT_FLOAT_EQ(descriptor.image.uvMax.y, 0.4375f);
|
||||
|
||||
surfaceRegistry.BeginFrame();
|
||||
|
||||
ASSERT_TRUE(surfaceRegistry.TryGetSurfaceDescriptor("XCUI Demo", descriptor));
|
||||
EXPECT_FALSE(descriptor.queuedThisFrame);
|
||||
EXPECT_TRUE(descriptor.image.IsValid());
|
||||
}
|
||||
|
||||
TEST(XCUIHostedPreviewPresenterTest, SurfaceRegistryRejectsInvalidSurfaceUpdatesWithoutClobberingExistingImage) {
|
||||
XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {};
|
||||
XCUIHostedPreviewSurfaceDescriptor descriptor = {};
|
||||
|
||||
surfaceRegistry.UpdateSurface(
|
||||
"XCUI Demo",
|
||||
static_cast<ImTextureID>(static_cast<intptr_t>(17)),
|
||||
512u,
|
||||
256u,
|
||||
XCEngine::UI::UIRect(64.0f, 32.0f, 256.0f, 128.0f));
|
||||
|
||||
ASSERT_TRUE(surfaceRegistry.TryGetSurfaceDescriptor("XCUI Demo", descriptor));
|
||||
ASSERT_TRUE(descriptor.image.IsValid());
|
||||
const XCUIHostedPreviewSurfaceImage originalImage = descriptor.image;
|
||||
|
||||
surfaceRegistry.UpdateSurface(
|
||||
"XCUI Demo",
|
||||
ImTextureID{},
|
||||
512u,
|
||||
256u,
|
||||
XCEngine::UI::UIRect(0.0f, 0.0f, 128.0f, 64.0f));
|
||||
surfaceRegistry.UpdateSurface(
|
||||
"",
|
||||
static_cast<ImTextureID>(static_cast<intptr_t>(19)),
|
||||
512u,
|
||||
256u,
|
||||
XCEngine::UI::UIRect(0.0f, 0.0f, 128.0f, 64.0f));
|
||||
surfaceRegistry.UpdateSurface(
|
||||
"XCUI Demo",
|
||||
static_cast<ImTextureID>(static_cast<intptr_t>(21)),
|
||||
0u,
|
||||
256u,
|
||||
XCEngine::UI::UIRect(0.0f, 0.0f, 128.0f, 64.0f));
|
||||
|
||||
ASSERT_TRUE(surfaceRegistry.TryGetSurfaceDescriptor("XCUI Demo", descriptor));
|
||||
EXPECT_EQ(descriptor.image.textureId, originalImage.textureId);
|
||||
EXPECT_EQ(descriptor.image.surfaceWidth, originalImage.surfaceWidth);
|
||||
EXPECT_EQ(descriptor.image.surfaceHeight, originalImage.surfaceHeight);
|
||||
EXPECT_FLOAT_EQ(descriptor.image.uvMin.x, originalImage.uvMin.x);
|
||||
EXPECT_FLOAT_EQ(descriptor.image.uvMin.y, originalImage.uvMin.y);
|
||||
EXPECT_FLOAT_EQ(descriptor.image.uvMax.x, originalImage.uvMax.x);
|
||||
EXPECT_FLOAT_EQ(descriptor.image.uvMax.y, originalImage.uvMax.y);
|
||||
}
|
||||
|
||||
TEST(XCUIHostedPreviewPresenterTest, BeginFrameClearsQueuedFramesButKeepsLastDrainStatsUntilReplaced) {
|
||||
XCUIHostedPreviewQueue queue = {};
|
||||
|
||||
XCEngine::UI::UIDrawData drawData = {};
|
||||
XCEngine::UI::UIDrawList& drawList = drawData.EmplaceDrawList("Preview");
|
||||
drawList.AddFilledRect(
|
||||
XCEngine::UI::UIRect(4.0f, 4.0f, 18.0f, 12.0f),
|
||||
XCEngine::UI::UIColor(0.4f, 0.6f, 0.9f, 1.0f));
|
||||
|
||||
XCUIHostedPreviewFrame frame = {};
|
||||
frame.drawData = &drawData;
|
||||
ASSERT_TRUE(queue.Submit(frame));
|
||||
ASSERT_EQ(queue.GetQueuedFrames().size(), 1u);
|
||||
|
||||
XCUIHostedPreviewDrainStats initialDrainStats = {};
|
||||
initialDrainStats.queuedFrameCount = 3u;
|
||||
initialDrainStats.queuedCommandCount = 7u;
|
||||
initialDrainStats.renderedFrameCount = 2u;
|
||||
initialDrainStats.skippedFrameCount = 1u;
|
||||
queue.SetLastDrainStats(initialDrainStats);
|
||||
|
||||
queue.BeginFrame();
|
||||
|
||||
EXPECT_TRUE(queue.GetQueuedFrames().empty());
|
||||
const XCUIHostedPreviewDrainStats& drainStats = queue.GetLastDrainStats();
|
||||
EXPECT_EQ(drainStats.queuedFrameCount, 3u);
|
||||
EXPECT_EQ(drainStats.queuedCommandCount, 7u);
|
||||
EXPECT_EQ(drainStats.renderedFrameCount, 2u);
|
||||
EXPECT_EQ(drainStats.skippedFrameCount, 1u);
|
||||
}
|
||||
|
||||
TEST(XCUIHostedPreviewPresenterTest, SurfaceRegistryQueriesClearOutputForMissingOrInvalidNames) {
|
||||
XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {};
|
||||
XCUIHostedPreviewSurfaceDescriptor descriptor = {};
|
||||
descriptor.debugName = "stale";
|
||||
descriptor.debugSource = "stale";
|
||||
descriptor.queuedThisFrame = true;
|
||||
|
||||
XCUIHostedPreviewSurfaceImage image = {};
|
||||
image.textureId = static_cast<ImTextureID>(static_cast<intptr_t>(23));
|
||||
image.surfaceWidth = 64u;
|
||||
image.surfaceHeight = 64u;
|
||||
|
||||
EXPECT_FALSE(surfaceRegistry.TryGetSurfaceDescriptor(nullptr, descriptor));
|
||||
EXPECT_TRUE(descriptor.debugName.empty());
|
||||
EXPECT_TRUE(descriptor.debugSource.empty());
|
||||
EXPECT_FALSE(descriptor.queuedThisFrame);
|
||||
EXPECT_FALSE(descriptor.image.IsValid());
|
||||
|
||||
EXPECT_FALSE(surfaceRegistry.TryGetSurfaceImage("", image));
|
||||
EXPECT_FALSE(image.IsValid());
|
||||
EXPECT_EQ(image.surfaceWidth, 0u);
|
||||
EXPECT_EQ(image.surfaceHeight, 0u);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
146
tests/NewEditor/test_xcui_input_bridge.cpp
Normal file
146
tests/NewEditor/test_xcui_input_bridge.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "XCUIBackend/XCUIInputBridge.h"
|
||||
|
||||
#include <XCEngine/Input/InputTypes.h>
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::Editor::XCUIBackend::XCUIInputBridge;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIInputBridgeCaptureOptions;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameDelta;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIInputBridgeKeyState;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIWin32InputSource;
|
||||
using XCEngine::Input::KeyCode;
|
||||
using XCEngine::UI::UIInputEventType;
|
||||
using XCEngine::UI::UIPoint;
|
||||
|
||||
XCUIInputBridgeKeyState MakeKeyState(KeyCode keyCode, bool down, bool repeat = false) {
|
||||
XCUIInputBridgeKeyState state = {};
|
||||
state.keyCode = static_cast<std::int32_t>(keyCode);
|
||||
state.down = down;
|
||||
state.repeat = repeat;
|
||||
return state;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(XCUIInputBridgeTest, TranslateBuildsPointerKeyboardAndCharacterEventsFromSnapshots) {
|
||||
XCUIInputBridgeFrameSnapshot previous = {};
|
||||
|
||||
XCUIInputBridgeFrameSnapshot current = {};
|
||||
current.pointerInside = true;
|
||||
current.pointerPosition = UIPoint(32.0f, 48.0f);
|
||||
current.pointerButtonsDown[0] = true;
|
||||
current.windowFocused = true;
|
||||
current.keys.push_back(MakeKeyState(KeyCode::P, true));
|
||||
current.characters.push_back(static_cast<std::uint32_t>('p'));
|
||||
|
||||
const XCUIInputBridgeFrameDelta delta = XCUIInputBridge::Translate(previous, current);
|
||||
|
||||
EXPECT_TRUE(delta.focusGained);
|
||||
EXPECT_TRUE(delta.pointer.entered);
|
||||
EXPECT_TRUE(delta.pointer.moved);
|
||||
EXPECT_TRUE(delta.pointer.pressed[0]);
|
||||
ASSERT_EQ(delta.keyboard.pressedKeys.size(), 1u);
|
||||
EXPECT_EQ(delta.keyboard.pressedKeys[0], static_cast<std::int32_t>(KeyCode::P));
|
||||
ASSERT_EQ(delta.keyboard.characters.size(), 1u);
|
||||
EXPECT_EQ(delta.keyboard.characters[0], static_cast<std::uint32_t>('p'));
|
||||
|
||||
EXPECT_TRUE(delta.HasEventType(UIInputEventType::FocusGained));
|
||||
EXPECT_TRUE(delta.HasEventType(UIInputEventType::PointerEnter));
|
||||
EXPECT_TRUE(delta.HasEventType(UIInputEventType::PointerMove));
|
||||
EXPECT_TRUE(delta.HasEventType(UIInputEventType::PointerButtonDown));
|
||||
EXPECT_TRUE(delta.HasEventType(UIInputEventType::KeyDown));
|
||||
EXPECT_TRUE(delta.HasEventType(UIInputEventType::Character));
|
||||
}
|
||||
|
||||
TEST(XCUIInputBridgeTest, PrimeSuppressesSyntheticFirstFrameTransitions) {
|
||||
XCUIInputBridge bridge = {};
|
||||
|
||||
XCUIInputBridgeFrameSnapshot baseline = {};
|
||||
baseline.pointerInside = true;
|
||||
baseline.pointerPosition = UIPoint(24.0f, 12.0f);
|
||||
baseline.pointerButtonsDown[0] = true;
|
||||
baseline.windowFocused = true;
|
||||
baseline.keys.push_back(MakeKeyState(KeyCode::P, true));
|
||||
|
||||
bridge.Prime(baseline);
|
||||
|
||||
const XCUIInputBridgeFrameDelta firstDelta = bridge.Translate(baseline);
|
||||
EXPECT_FALSE(firstDelta.HasEvents());
|
||||
|
||||
XCUIInputBridgeFrameSnapshot released = baseline;
|
||||
released.pointerButtonsDown[0] = false;
|
||||
released.keys.clear();
|
||||
|
||||
const XCUIInputBridgeFrameDelta secondDelta = bridge.Translate(released);
|
||||
EXPECT_TRUE(secondDelta.pointer.released[0]);
|
||||
ASSERT_EQ(secondDelta.keyboard.releasedKeys.size(), 1u);
|
||||
EXPECT_EQ(secondDelta.keyboard.releasedKeys[0], static_cast<std::int32_t>(KeyCode::P));
|
||||
EXPECT_TRUE(secondDelta.HasEventType(UIInputEventType::PointerButtonUp));
|
||||
EXPECT_TRUE(secondDelta.HasEventType(UIInputEventType::KeyUp));
|
||||
}
|
||||
|
||||
TEST(XCUIInputBridgeTest, Win32InputSourceCapturesPointerWheelKeyRepeatAndCharacters) {
|
||||
XCUIWin32InputSource inputSource = {};
|
||||
|
||||
inputSource.HandleWindowMessage(nullptr, WM_SETFOCUS, 0, 0);
|
||||
inputSource.HandleWindowMessage(nullptr, WM_MOUSEMOVE, 0, MAKELPARAM(64, 96));
|
||||
inputSource.HandleWindowMessage(nullptr, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(64, 96));
|
||||
inputSource.HandleWindowMessage(nullptr, WM_MOUSEWHEEL, MAKEWPARAM(0, WHEEL_DELTA), 0);
|
||||
inputSource.HandleWindowMessage(nullptr, WM_KEYDOWN, 'P', 0);
|
||||
inputSource.HandleWindowMessage(nullptr, WM_KEYDOWN, 'P', 1u << 30u);
|
||||
inputSource.HandleWindowMessage(nullptr, WM_CHAR, 'p', 0);
|
||||
|
||||
XCUIInputBridgeCaptureOptions options = {};
|
||||
options.windowFocused = true;
|
||||
const XCUIInputBridgeFrameSnapshot snapshot = inputSource.CaptureSnapshot(options);
|
||||
|
||||
EXPECT_TRUE(snapshot.windowFocused);
|
||||
EXPECT_TRUE(snapshot.pointerInside);
|
||||
EXPECT_EQ(snapshot.pointerPosition.x, 64.0f);
|
||||
EXPECT_EQ(snapshot.pointerPosition.y, 96.0f);
|
||||
EXPECT_TRUE(snapshot.pointerButtonsDown[0]);
|
||||
EXPECT_FLOAT_EQ(snapshot.wheelDelta.y, 1.0f);
|
||||
EXPECT_TRUE(snapshot.IsKeyDown(static_cast<std::int32_t>(KeyCode::P)));
|
||||
ASSERT_EQ(snapshot.characters.size(), 1u);
|
||||
EXPECT_EQ(snapshot.characters[0], static_cast<std::uint32_t>('p'));
|
||||
|
||||
const XCUIInputBridgeFrameDelta delta = XCUIInputBridge::Translate(XCUIInputBridgeFrameSnapshot(), snapshot);
|
||||
EXPECT_TRUE(delta.pointer.pressed[0]);
|
||||
ASSERT_EQ(delta.keyboard.pressedKeys.size(), 1u);
|
||||
EXPECT_EQ(delta.keyboard.pressedKeys[0], static_cast<std::int32_t>(KeyCode::P));
|
||||
EXPECT_TRUE(delta.HasEventType(UIInputEventType::Character));
|
||||
|
||||
inputSource.ClearFrameTransients();
|
||||
const XCUIInputBridgeFrameSnapshot afterClear = inputSource.CaptureSnapshot(options);
|
||||
EXPECT_FLOAT_EQ(afterClear.wheelDelta.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(afterClear.wheelDelta.y, 0.0f);
|
||||
EXPECT_TRUE(afterClear.characters.empty());
|
||||
const XCUIInputBridgeKeyState* repeatedKey =
|
||||
afterClear.FindKeyState(static_cast<std::int32_t>(KeyCode::P));
|
||||
ASSERT_NE(repeatedKey, nullptr);
|
||||
EXPECT_FALSE(repeatedKey->repeat);
|
||||
}
|
||||
|
||||
TEST(XCUIInputBridgeTest, Win32InputSourceClearsPressedStateOnFocusLoss) {
|
||||
XCUIWin32InputSource inputSource = {};
|
||||
|
||||
inputSource.HandleWindowMessage(nullptr, WM_SETFOCUS, 0, 0);
|
||||
inputSource.HandleWindowMessage(nullptr, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(20, 24));
|
||||
inputSource.HandleWindowMessage(nullptr, WM_KEYDOWN, 'P', 0);
|
||||
|
||||
XCUIInputBridgeCaptureOptions options = {};
|
||||
options.windowFocused = true;
|
||||
XCUIInputBridgeFrameSnapshot focused = inputSource.CaptureSnapshot(options);
|
||||
EXPECT_TRUE(focused.pointerButtonsDown[0]);
|
||||
EXPECT_TRUE(focused.IsKeyDown(static_cast<std::int32_t>(KeyCode::P)));
|
||||
|
||||
inputSource.HandleWindowMessage(nullptr, WM_KILLFOCUS, 0, 0);
|
||||
XCUIInputBridgeFrameSnapshot blurred = inputSource.CaptureSnapshot(options);
|
||||
EXPECT_FALSE(blurred.windowFocused);
|
||||
EXPECT_FALSE(blurred.pointerButtonsDown[0]);
|
||||
EXPECT_FALSE(blurred.IsKeyDown(static_cast<std::int32_t>(KeyCode::P)));
|
||||
}
|
||||
192
tests/NewEditor/test_xcui_layout_lab_runtime.cpp
Normal file
192
tests/NewEditor/test_xcui_layout_lab_runtime.cpp
Normal file
@@ -0,0 +1,192 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "XCUIBackend/XCUILayoutLabRuntime.h"
|
||||
|
||||
#include <XCEngine/UI/Types.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<const UIDrawCommand*> CollectTextCommands(const XCEngine::UI::UIDrawData& drawData) {
|
||||
std::vector<const UIDrawCommand*> 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<const UIDrawCommand*> 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());
|
||||
}
|
||||
242
tests/NewEditor/test_xcui_rhi_command_compiler.cpp
Normal file
242
tests/NewEditor/test_xcui_rhi_command_compiler.cpp
Normal file
@@ -0,0 +1,242 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "XCUIBackend/XCUIRHICommandCompiler.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::Editor::XCUIBackend::XCUIRHICommandCompiler;
|
||||
using XCEngine::UI::UIColor;
|
||||
using XCEngine::UI::UIDrawData;
|
||||
using XCEngine::UI::UIDrawList;
|
||||
using XCEngine::UI::UIPoint;
|
||||
using XCEngine::UI::UIRect;
|
||||
using XCEngine::UI::UITextureHandle;
|
||||
using XCEngine::UI::UITextureHandleKind;
|
||||
|
||||
class StubTextGlyphProvider final : public XCUIRHICommandCompiler::TextGlyphProvider {
|
||||
public:
|
||||
bool BeginText(
|
||||
float requestedFontSize,
|
||||
XCUIRHICommandCompiler::TextRunContext& outContext) const override {
|
||||
outContext.requestedFontSize = requestedFontSize;
|
||||
outContext.resolvedFontSize = requestedFontSize > 0.0f ? requestedFontSize : 14.0f;
|
||||
outContext.lineHeight = 12.0f;
|
||||
outContext.texture = UITextureHandle{ 99u, 256u, 256u, UITextureHandleKind::ShaderResourceView };
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResolveGlyph(
|
||||
const XCUIRHICommandCompiler::TextRunContext&,
|
||||
std::uint32_t codepoint,
|
||||
XCUIRHICommandCompiler::TextGlyph& outGlyph) const override {
|
||||
switch (codepoint) {
|
||||
case 'A':
|
||||
outGlyph.x0 = 0.0f;
|
||||
outGlyph.y0 = 0.0f;
|
||||
outGlyph.x1 = 8.0f;
|
||||
outGlyph.y1 = 10.0f;
|
||||
outGlyph.u0 = 0.0f;
|
||||
outGlyph.v0 = 0.0f;
|
||||
outGlyph.u1 = 0.25f;
|
||||
outGlyph.v1 = 0.5f;
|
||||
outGlyph.advanceX = 8.0f;
|
||||
outGlyph.visible = true;
|
||||
return true;
|
||||
case 'B':
|
||||
outGlyph.x0 = 0.0f;
|
||||
outGlyph.y0 = 0.0f;
|
||||
outGlyph.x1 = 7.0f;
|
||||
outGlyph.y1 = 10.0f;
|
||||
outGlyph.u0 = 0.25f;
|
||||
outGlyph.v0 = 0.0f;
|
||||
outGlyph.u1 = 0.5f;
|
||||
outGlyph.v1 = 0.5f;
|
||||
outGlyph.advanceX = 7.0f;
|
||||
outGlyph.visible = true;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST(XCUIRHICommandCompilerTest, CompileMergesAdjacentColorAndTexturedCommandsPerDrawList) {
|
||||
XCUIRHICommandCompiler compiler = {};
|
||||
UIDrawData drawData = {};
|
||||
UIDrawList& drawList = drawData.EmplaceDrawList("Batches");
|
||||
drawList.AddFilledRect(UIRect(0.0f, 0.0f, 10.0f, 10.0f), UIColor(1.0f, 0.0f, 0.0f, 1.0f));
|
||||
drawList.AddFilledRect(UIRect(10.0f, 0.0f, 10.0f, 10.0f), UIColor(0.0f, 1.0f, 0.0f, 1.0f));
|
||||
|
||||
const UITextureHandle texture{ 7u, 64u, 64u, UITextureHandleKind::ShaderResourceView };
|
||||
drawList.AddImage(UIRect(0.0f, 20.0f, 10.0f, 10.0f), texture, UIColor(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
drawList.AddImage(UIRect(10.0f, 20.0f, 10.0f, 10.0f), texture, UIColor(0.5f, 0.5f, 0.5f, 1.0f));
|
||||
|
||||
XCUIRHICommandCompiler::CompileConfig config = {};
|
||||
config.surfaceClipRect = UIRect(0.0f, 0.0f, 100.0f, 100.0f);
|
||||
|
||||
XCUIRHICommandCompiler::CompiledDrawData compiled = {};
|
||||
compiler.Compile(drawData, config, compiled);
|
||||
|
||||
ASSERT_EQ(compiled.batches.size(), 2u);
|
||||
EXPECT_EQ(compiled.batches[0].kind, XCUIRHICommandCompiler::BatchKind::Color);
|
||||
EXPECT_EQ(compiled.batches[0].commandCount, 2u);
|
||||
EXPECT_EQ(compiled.batches[0].vertexCount, 12u);
|
||||
EXPECT_EQ(compiled.batches[1].kind, XCUIRHICommandCompiler::BatchKind::Textured);
|
||||
EXPECT_EQ(compiled.batches[1].commandCount, 2u);
|
||||
EXPECT_EQ(compiled.batches[1].vertexCount, 12u);
|
||||
EXPECT_EQ(compiled.batches[1].texture.nativeHandle, texture.nativeHandle);
|
||||
EXPECT_EQ(compiled.stats.compiledCommandCount, 4u);
|
||||
EXPECT_EQ(compiled.stats.batchCount, 2u);
|
||||
EXPECT_EQ(compiled.stats.colorVertexCount, 12u);
|
||||
EXPECT_EQ(compiled.stats.texturedVertexCount, 12u);
|
||||
}
|
||||
|
||||
TEST(XCUIRHICommandCompilerTest, CompileTracksClipStackTransitionsAndUnderflow) {
|
||||
XCUIRHICommandCompiler compiler = {};
|
||||
UIDrawData drawData = {};
|
||||
UIDrawList& drawList = drawData.EmplaceDrawList("ClipStack");
|
||||
drawList.PushClipRect(UIRect(0.0f, 0.0f, 50.0f, 50.0f));
|
||||
drawList.PushClipRect(UIRect(10.0f, 10.0f, 40.0f, 40.0f));
|
||||
drawList.AddFilledRect(UIRect(0.0f, 0.0f, 30.0f, 30.0f), UIColor(1.0f, 0.0f, 0.0f, 1.0f));
|
||||
drawList.PopClipRect();
|
||||
drawList.AddFilledRect(UIRect(0.0f, 0.0f, 20.0f, 20.0f), UIColor(0.0f, 1.0f, 0.0f, 1.0f));
|
||||
drawList.PopClipRect();
|
||||
drawList.PopClipRect();
|
||||
|
||||
XCUIRHICommandCompiler::CompileConfig config = {};
|
||||
config.surfaceClipRect = UIRect(0.0f, 0.0f, 100.0f, 100.0f);
|
||||
|
||||
XCUIRHICommandCompiler::CompiledDrawData compiled = {};
|
||||
compiler.Compile(drawData, config, compiled);
|
||||
|
||||
ASSERT_EQ(compiled.batches.size(), 2u);
|
||||
EXPECT_EQ(compiled.stats.compiledCommandCount, 7u);
|
||||
EXPECT_EQ(compiled.stats.clipPushCommandCount, 2u);
|
||||
EXPECT_EQ(compiled.stats.clipPopCommandCount, 3u);
|
||||
EXPECT_EQ(compiled.stats.clipStackUnderflowCount, 1u);
|
||||
EXPECT_EQ(compiled.stats.maxClipDepth, 2u);
|
||||
EXPECT_EQ(compiled.stats.danglingClipDepth, 0u);
|
||||
EXPECT_FLOAT_EQ(compiled.batches[0].clipRect.x, 10.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.batches[0].clipRect.y, 10.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.batches[0].clipRect.width, 40.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.batches[0].clipRect.height, 40.0f);
|
||||
ASSERT_GE(compiled.colorVertices.size(), 12u);
|
||||
EXPECT_FLOAT_EQ(compiled.colorVertices[0].position[0], 10.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.colorVertices[0].position[1], 10.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.colorVertices[6].position[0], 0.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.colorVertices[6].position[1], 0.0f);
|
||||
}
|
||||
|
||||
TEST(XCUIRHICommandCompilerTest, CompileUsesTextGlyphProviderOutputForTextBatches) {
|
||||
XCUIRHICommandCompiler compiler = {};
|
||||
StubTextGlyphProvider glyphProvider = {};
|
||||
|
||||
UIDrawData drawData = {};
|
||||
UIDrawList& drawList = drawData.EmplaceDrawList("Text");
|
||||
drawList.AddText(UIPoint(4.0f, 6.0f), "A\nB", UIColor(1.0f, 1.0f, 1.0f, 1.0f), 16.0f);
|
||||
|
||||
XCUIRHICommandCompiler::CompileConfig config = {};
|
||||
config.surfaceClipRect = UIRect(0.0f, 0.0f, 100.0f, 100.0f);
|
||||
config.textGlyphProvider = &glyphProvider;
|
||||
|
||||
XCUIRHICommandCompiler::CompiledDrawData compiled = {};
|
||||
compiler.Compile(drawData, config, compiled);
|
||||
|
||||
ASSERT_EQ(compiled.batches.size(), 1u);
|
||||
EXPECT_EQ(compiled.batches[0].kind, XCUIRHICommandCompiler::BatchKind::Textured);
|
||||
EXPECT_EQ(compiled.batches[0].vertexCount, 12u);
|
||||
EXPECT_EQ(compiled.batches[0].texture.nativeHandle, 99u);
|
||||
EXPECT_EQ(compiled.stats.textCommandCount, 1u);
|
||||
EXPECT_EQ(compiled.stats.compiledCommandCount, 1u);
|
||||
ASSERT_EQ(compiled.texturedVertices.size(), 12u);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[0].position[0], 4.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[0].position[1], 6.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[6].position[0], 4.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[6].position[1], 18.0f);
|
||||
}
|
||||
|
||||
TEST(XCUIRHICommandCompilerTest, CompileReportsUnsupportedTextWhenNoGlyphProviderIsAvailable) {
|
||||
XCUIRHICommandCompiler compiler = {};
|
||||
UIDrawData drawData = {};
|
||||
UIDrawList& drawList = drawData.EmplaceDrawList("UnsupportedText");
|
||||
drawList.AddText(UIPoint(0.0f, 0.0f), "xcui", UIColor(1.0f, 1.0f, 1.0f, 1.0f), 12.0f);
|
||||
|
||||
XCUIRHICommandCompiler::CompileConfig config = {};
|
||||
config.surfaceClipRect = UIRect(0.0f, 0.0f, 32.0f, 32.0f);
|
||||
|
||||
XCUIRHICommandCompiler::CompiledDrawData compiled = {};
|
||||
compiler.Compile(drawData, config, compiled);
|
||||
|
||||
EXPECT_TRUE(compiled.Empty());
|
||||
EXPECT_EQ(compiled.stats.textCommandCount, 1u);
|
||||
EXPECT_EQ(compiled.stats.compiledCommandCount, 0u);
|
||||
EXPECT_EQ(compiled.stats.skippedCommandCount, 1u);
|
||||
EXPECT_EQ(compiled.stats.unsupportedCommandCount, 1u);
|
||||
}
|
||||
|
||||
TEST(XCUIRHICommandCompilerTest, CompileClipsImageCommandsAndAdjustsUvCoordinates) {
|
||||
XCUIRHICommandCompiler compiler = {};
|
||||
UIDrawData drawData = {};
|
||||
UIDrawList& drawList = drawData.EmplaceDrawList("ClippedImage");
|
||||
drawList.PushClipRect(UIRect(10.0f, 8.0f, 12.0f, 10.0f));
|
||||
drawList.AddImage(
|
||||
UIRect(4.0f, 4.0f, 20.0f, 20.0f),
|
||||
UITextureHandle{ 77u, 32u, 32u, UITextureHandleKind::ShaderResourceView },
|
||||
UIColor(0.7f, 0.8f, 0.9f, 1.0f));
|
||||
|
||||
XCUIRHICommandCompiler::CompileConfig config = {};
|
||||
config.surfaceClipRect = UIRect(0.0f, 0.0f, 64.0f, 64.0f);
|
||||
|
||||
XCUIRHICommandCompiler::CompiledDrawData compiled = {};
|
||||
compiler.Compile(drawData, config, compiled);
|
||||
|
||||
ASSERT_EQ(compiled.batches.size(), 1u);
|
||||
ASSERT_EQ(compiled.texturedVertices.size(), 6u);
|
||||
EXPECT_EQ(compiled.batches[0].kind, XCUIRHICommandCompiler::BatchKind::Textured);
|
||||
EXPECT_FLOAT_EQ(compiled.batches[0].clipRect.x, 10.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.batches[0].clipRect.y, 8.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.batches[0].clipRect.width, 12.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.batches[0].clipRect.height, 10.0f);
|
||||
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[0].position[0], 10.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[0].position[1], 8.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[0].uv[0], 0.3f);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[0].uv[1], 0.2f);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[1].position[0], 22.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[1].uv[0], 0.9f);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[2].position[1], 18.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[2].uv[1], 0.7f);
|
||||
EXPECT_EQ(compiled.stats.imageCommandCount, 1u);
|
||||
EXPECT_EQ(compiled.stats.compiledCommandCount, 2u);
|
||||
}
|
||||
|
||||
TEST(XCUIRHICommandCompilerTest, CompilePreservesMirroredImageUvForNegativeRectExtents) {
|
||||
XCUIRHICommandCompiler compiler = {};
|
||||
UIDrawData drawData = {};
|
||||
UIDrawList& drawList = drawData.EmplaceDrawList("MirroredImage");
|
||||
drawList.AddImage(
|
||||
UIRect(24.0f, 18.0f, -16.0f, -12.0f),
|
||||
UITextureHandle{ 91u, 64u, 64u, UITextureHandleKind::ShaderResourceView },
|
||||
UIColor(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
|
||||
XCUIRHICommandCompiler::CompileConfig config = {};
|
||||
config.surfaceClipRect = UIRect(0.0f, 0.0f, 64.0f, 64.0f);
|
||||
|
||||
XCUIRHICommandCompiler::CompiledDrawData compiled = {};
|
||||
compiler.Compile(drawData, config, compiled);
|
||||
|
||||
ASSERT_EQ(compiled.batches.size(), 1u);
|
||||
ASSERT_EQ(compiled.texturedVertices.size(), 6u);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[0].position[0], 8.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[0].position[1], 6.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[0].uv[0], 1.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[0].uv[1], 1.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[1].position[0], 24.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[1].uv[0], 0.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[2].position[1], 18.0f);
|
||||
EXPECT_FLOAT_EQ(compiled.texturedVertices[2].uv[1], 0.0f);
|
||||
EXPECT_EQ(compiled.stats.imageCommandCount, 1u);
|
||||
EXPECT_EQ(compiled.stats.compiledCommandCount, 1u);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
202
tests/NewEditor/test_xcui_rhi_command_support.cpp
Normal file
202
tests/NewEditor/test_xcui_rhi_command_support.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "XCUIBackend/XCUIRHICommandSupport.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::Editor::XCUIBackend::AccumulateXCUIRHICommandSupport;
|
||||
using XCEngine::Editor::XCUIBackend::AnalyzeXCUIRHICommandSupport;
|
||||
using XCEngine::Editor::XCUIBackend::BuildXCUIRHICommandSupportDiagnostic;
|
||||
using XCEngine::Editor::XCUIBackend::ClassifyXCUIRHICommandSupport;
|
||||
using XCEngine::Editor::XCUIBackend::SummarizeXCUIRHICommandSupport;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIRHICommandCategory;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIRHICommandDiagnosticOptions;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIRHICommandSupportReason;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIRHICommandSupportStats;
|
||||
using XCEngine::UI::UIColor;
|
||||
using XCEngine::UI::UIDrawCommand;
|
||||
using XCEngine::UI::UIDrawCommandType;
|
||||
using XCEngine::UI::UIDrawData;
|
||||
using XCEngine::UI::UIDrawList;
|
||||
using XCEngine::UI::UIPoint;
|
||||
using XCEngine::UI::UIRect;
|
||||
using XCEngine::UI::UITextureHandle;
|
||||
using XCEngine::UI::UITextureHandleKind;
|
||||
|
||||
UITextureHandle MakeShaderResourceTexture() {
|
||||
UITextureHandle texture = {};
|
||||
texture.nativeHandle = 42u;
|
||||
texture.width = 64u;
|
||||
texture.height = 64u;
|
||||
texture.kind = UITextureHandleKind::ShaderResourceView;
|
||||
return texture;
|
||||
}
|
||||
|
||||
UIDrawCommand MakeUnknownCommand() {
|
||||
UIDrawCommand command = {};
|
||||
command.type = static_cast<UIDrawCommandType>(255);
|
||||
return command;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(XCUIRHICommandSupportTest, ClassifySupportedCommandTypes) {
|
||||
UIDrawList drawList("Supported");
|
||||
drawList.AddFilledRect(UIRect(0.0f, 0.0f, 12.0f, 10.0f), UIColor(1.0f, 0.0f, 0.0f, 1.0f));
|
||||
drawList.AddRectOutline(
|
||||
UIRect(1.0f, 2.0f, 8.0f, 5.0f),
|
||||
UIColor(0.0f, 1.0f, 0.0f, 1.0f),
|
||||
2.0f,
|
||||
1.0f);
|
||||
drawList.AddText(UIPoint(4.0f, 5.0f), "label", UIColor(1.0f, 1.0f, 1.0f, 1.0f), 14.0f);
|
||||
drawList.AddImage(
|
||||
UIRect(2.0f, 3.0f, 18.0f, 14.0f),
|
||||
MakeShaderResourceTexture(),
|
||||
UIColor(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
drawList.PushClipRect(UIRect(0.0f, 0.0f, 24.0f, 20.0f));
|
||||
drawList.PopClipRect();
|
||||
|
||||
const auto& commands = drawList.GetCommands();
|
||||
|
||||
EXPECT_EQ(
|
||||
ClassifyXCUIRHICommandSupport(commands[0]).category,
|
||||
XCUIRHICommandCategory::FilledRect);
|
||||
EXPECT_EQ(
|
||||
ClassifyXCUIRHICommandSupport(commands[1]).category,
|
||||
XCUIRHICommandCategory::RectOutline);
|
||||
EXPECT_EQ(
|
||||
ClassifyXCUIRHICommandSupport(commands[2]).category,
|
||||
XCUIRHICommandCategory::Text);
|
||||
EXPECT_EQ(
|
||||
ClassifyXCUIRHICommandSupport(commands[3]).category,
|
||||
XCUIRHICommandCategory::Image);
|
||||
EXPECT_EQ(
|
||||
ClassifyXCUIRHICommandSupport(commands[4]).category,
|
||||
XCUIRHICommandCategory::PushClipRect);
|
||||
EXPECT_EQ(
|
||||
ClassifyXCUIRHICommandSupport(commands[5]).category,
|
||||
XCUIRHICommandCategory::PopClipRect);
|
||||
|
||||
for (const UIDrawCommand& command : commands) {
|
||||
EXPECT_TRUE(ClassifyXCUIRHICommandSupport(command).IsSupported());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(XCUIRHICommandSupportTest, InvalidImageCommandIsClassifiedAsUnsupportedTexture) {
|
||||
UIDrawCommand command = {};
|
||||
command.type = UIDrawCommandType::Image;
|
||||
command.rect = UIRect(0.0f, 0.0f, 12.0f, 12.0f);
|
||||
command.texture.nativeHandle = 7u;
|
||||
command.texture.width = 32u;
|
||||
command.texture.height = 32u;
|
||||
command.texture.kind = UITextureHandleKind::ImGuiDescriptor;
|
||||
|
||||
const auto classification = ClassifyXCUIRHICommandSupport(command);
|
||||
|
||||
EXPECT_EQ(classification.category, XCUIRHICommandCategory::Image);
|
||||
EXPECT_EQ(
|
||||
classification.supportReason,
|
||||
XCUIRHICommandSupportReason::UnsupportedImageTexture);
|
||||
EXPECT_FALSE(classification.IsSupported());
|
||||
}
|
||||
|
||||
TEST(XCUIRHICommandSupportTest, UnknownCommandTypeIsClassifiedSeparately) {
|
||||
const auto classification = ClassifyXCUIRHICommandSupport(MakeUnknownCommand());
|
||||
|
||||
EXPECT_EQ(classification.category, XCUIRHICommandCategory::Unknown);
|
||||
EXPECT_EQ(
|
||||
classification.supportReason,
|
||||
XCUIRHICommandSupportReason::UnsupportedUnknownCommand);
|
||||
EXPECT_FALSE(classification.IsSupported());
|
||||
}
|
||||
|
||||
TEST(XCUIRHICommandSupportTest, AnalyzeDrawDataAggregatesSupportedAndUnsupportedCounts) {
|
||||
UIDrawData drawData = {};
|
||||
|
||||
UIDrawList& firstDrawList = drawData.EmplaceDrawList("First");
|
||||
firstDrawList.AddFilledRect(
|
||||
UIRect(0.0f, 0.0f, 8.0f, 8.0f),
|
||||
UIColor(1.0f, 0.0f, 0.0f, 1.0f));
|
||||
firstDrawList.AddImage(
|
||||
UIRect(2.0f, 2.0f, 12.0f, 10.0f),
|
||||
MakeShaderResourceTexture(),
|
||||
UIColor(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
|
||||
UIDrawList& secondDrawList = drawData.EmplaceDrawList("Second");
|
||||
secondDrawList.PushClipRect(UIRect(0.0f, 0.0f, 100.0f, 60.0f));
|
||||
secondDrawList.AddText(
|
||||
UIPoint(5.0f, 6.0f),
|
||||
"status",
|
||||
UIColor(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
12.0f);
|
||||
|
||||
UITextureHandle invalidTexture = {};
|
||||
invalidTexture.nativeHandle = 9u;
|
||||
invalidTexture.width = 32u;
|
||||
invalidTexture.height = 32u;
|
||||
invalidTexture.kind = UITextureHandleKind::ImGuiDescriptor;
|
||||
secondDrawList.AddImage(
|
||||
UIRect(1.0f, 1.0f, 6.0f, 6.0f),
|
||||
invalidTexture,
|
||||
UIColor(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
|
||||
const auto stats = AnalyzeXCUIRHICommandSupport(drawData);
|
||||
|
||||
EXPECT_EQ(stats.drawListCount, 2u);
|
||||
EXPECT_EQ(stats.commandCount, 5u);
|
||||
EXPECT_EQ(stats.filledRectCommandCount, 1u);
|
||||
EXPECT_EQ(stats.textCommandCount, 1u);
|
||||
EXPECT_EQ(stats.imageCommandCount, 2u);
|
||||
EXPECT_EQ(stats.clipPushCommandCount, 1u);
|
||||
EXPECT_EQ(stats.clipPopCommandCount, 0u);
|
||||
EXPECT_EQ(stats.supportedCommandCount, 4u);
|
||||
EXPECT_EQ(stats.unsupportedCommandCount, 1u);
|
||||
EXPECT_EQ(stats.unsupportedImageCommandCount, 1u);
|
||||
EXPECT_EQ(stats.unsupportedUnknownCommandCount, 0u);
|
||||
EXPECT_FALSE(stats.SupportsAllCommands());
|
||||
}
|
||||
|
||||
TEST(XCUIRHICommandSupportTest, DiagnosticIncludesUnsupportedReasonsAndCanBeCustomized) {
|
||||
XCUIRHICommandSupportStats stats = {};
|
||||
AccumulateXCUIRHICommandSupport(MakeUnknownCommand(), stats);
|
||||
|
||||
UIDrawCommand invalidImage = {};
|
||||
invalidImage.type = UIDrawCommandType::Image;
|
||||
invalidImage.texture.nativeHandle = 11u;
|
||||
invalidImage.texture.width = 16u;
|
||||
invalidImage.texture.height = 16u;
|
||||
invalidImage.texture.kind = UITextureHandleKind::ImGuiDescriptor;
|
||||
AccumulateXCUIRHICommandSupport(invalidImage, stats);
|
||||
|
||||
const std::string defaultDiagnostic = BuildXCUIRHICommandSupportDiagnostic(stats);
|
||||
EXPECT_NE(defaultDiagnostic.find("2 command(s) will be skipped by native overlay: "), std::string::npos);
|
||||
EXPECT_NE(defaultDiagnostic.find("1 image command(s) missing valid ShaderResourceView textures"), std::string::npos);
|
||||
EXPECT_NE(defaultDiagnostic.find("1 unknown command type(s)"), std::string::npos);
|
||||
|
||||
XCUIRHICommandDiagnosticOptions options = {};
|
||||
options.noCommandsMessage = "No overlay commands.";
|
||||
options.allSupportedMessage = "Everything supported.";
|
||||
options.unsupportedPrefix = "command(s) rejected:";
|
||||
|
||||
const std::string customDiagnostic = BuildXCUIRHICommandSupportDiagnostic(stats, options);
|
||||
EXPECT_NE(customDiagnostic.find("2 command(s) rejected:"), std::string::npos);
|
||||
}
|
||||
|
||||
TEST(XCUIRHICommandSupportTest, SummaryReturnsNoCommandAndAllSupportedMessages) {
|
||||
const auto emptySummary = SummarizeXCUIRHICommandSupport(UIDrawData());
|
||||
EXPECT_EQ(emptySummary.stats.commandCount, 0u);
|
||||
EXPECT_EQ(emptySummary.diagnostic, "Overlay runtime produced no commands.");
|
||||
|
||||
UIDrawData drawData = {};
|
||||
UIDrawList& drawList = drawData.EmplaceDrawList("Supported");
|
||||
drawList.AddFilledRect(
|
||||
UIRect(0.0f, 0.0f, 4.0f, 4.0f),
|
||||
UIColor(0.2f, 0.4f, 0.8f, 1.0f));
|
||||
|
||||
const auto supportedSummary = SummarizeXCUIRHICommandSupport(drawData);
|
||||
EXPECT_EQ(supportedSummary.stats.supportedCommandCount, 1u);
|
||||
EXPECT_TRUE(supportedSummary.stats.SupportsAllCommands());
|
||||
EXPECT_EQ(supportedSummary.diagnostic, "All commands preflight for native overlay.");
|
||||
}
|
||||
710
tests/NewEditor/test_xcui_rhi_render_backend.cpp
Normal file
710
tests/NewEditor/test_xcui_rhi_render_backend.cpp
Normal file
@@ -0,0 +1,710 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "XCUIBackend/XCUIRHIRenderBackend.h"
|
||||
|
||||
#include <XCEngine/Rendering/RenderContext.h>
|
||||
#include <XCEngine/Rendering/RenderSurface.h>
|
||||
#include <XCEngine/RHI/RHIBuffer.h>
|
||||
#include <XCEngine/RHI/RHICapabilities.h>
|
||||
#include <XCEngine/RHI/RHICommandList.h>
|
||||
#include <XCEngine/RHI/RHICommandQueue.h>
|
||||
#include <XCEngine/RHI/RHIDevice.h>
|
||||
#include <XCEngine/RHI/RHIDescriptorPool.h>
|
||||
#include <XCEngine/RHI/RHIDescriptorSet.h>
|
||||
#include <XCEngine/RHI/RHIPipelineLayout.h>
|
||||
#include <XCEngine/RHI/RHIPipelineState.h>
|
||||
#include <XCEngine/RHI/RHIResourceView.h>
|
||||
#include <XCEngine/RHI/RHISampler.h>
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::Editor::XCUIBackend::IXCUITextAtlasProvider;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIRHIRenderBackend;
|
||||
using XCEngine::Rendering::RenderContext;
|
||||
using XCEngine::Rendering::RenderSurface;
|
||||
using XCEngine::RHI::BlendDesc;
|
||||
using XCEngine::RHI::BufferDesc;
|
||||
using XCEngine::RHI::BufferType;
|
||||
using XCEngine::RHI::CommandQueueType;
|
||||
using XCEngine::RHI::ComparisonFunc;
|
||||
using XCEngine::RHI::DepthStencilStateDesc;
|
||||
using XCEngine::RHI::DescriptorHeapType;
|
||||
using XCEngine::RHI::DescriptorPoolDesc;
|
||||
using XCEngine::RHI::DescriptorSetLayoutBinding;
|
||||
using XCEngine::RHI::DescriptorSetLayoutDesc;
|
||||
using XCEngine::RHI::Format;
|
||||
using XCEngine::RHI::GraphicsPipelineDesc;
|
||||
using XCEngine::RHI::InputLayoutDesc;
|
||||
using XCEngine::RHI::PipelineStateHash;
|
||||
using XCEngine::RHI::PipelineType;
|
||||
using XCEngine::RHI::PrimitiveTopology;
|
||||
using XCEngine::RHI::RasterizerDesc;
|
||||
using XCEngine::RHI::Rect;
|
||||
using XCEngine::RHI::ResourceStates;
|
||||
using XCEngine::RHI::ResourceViewDesc;
|
||||
using XCEngine::RHI::ResourceViewDimension;
|
||||
using XCEngine::RHI::ResourceViewType;
|
||||
using XCEngine::RHI::RHICommandList;
|
||||
using XCEngine::RHI::RHICommandQueue;
|
||||
using XCEngine::RHI::RHIDescriptorPool;
|
||||
using XCEngine::RHI::RHIDescriptorSet;
|
||||
using XCEngine::RHI::RHIDevice;
|
||||
using XCEngine::RHI::RHIPipelineLayout;
|
||||
using XCEngine::RHI::RHIPipelineState;
|
||||
using XCEngine::RHI::RHIResourceView;
|
||||
using XCEngine::RHI::RHISampler;
|
||||
using XCEngine::RHI::RHIBuffer;
|
||||
using XCEngine::RHI::RHIShader;
|
||||
using XCEngine::RHI::RHIType;
|
||||
using XCEngine::RHI::SamplerDesc;
|
||||
using XCEngine::RHI::Viewport;
|
||||
using XCEngine::UI::UIColor;
|
||||
using XCEngine::UI::UIDrawData;
|
||||
using XCEngine::UI::UIDrawList;
|
||||
using XCEngine::UI::UIRect;
|
||||
using XCEngine::UI::UITextureHandle;
|
||||
using XCEngine::UI::UITextureHandleKind;
|
||||
|
||||
class FakeResourceViewBase {
|
||||
public:
|
||||
explicit FakeResourceViewBase(
|
||||
ResourceViewType viewType,
|
||||
ResourceViewDimension dimension = ResourceViewDimension::Texture2D,
|
||||
Format format = Format::R8G8B8A8_UNorm)
|
||||
: m_viewType(viewType)
|
||||
, m_dimension(dimension)
|
||||
, m_format(format) {
|
||||
}
|
||||
|
||||
void ShutdownBase() {
|
||||
m_valid = false;
|
||||
}
|
||||
|
||||
void* GetNativeHandleBase() {
|
||||
return this;
|
||||
}
|
||||
|
||||
bool IsValidBase() const {
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
ResourceViewType GetViewTypeBase() const {
|
||||
return m_viewType;
|
||||
}
|
||||
|
||||
ResourceViewDimension GetDimensionBase() const {
|
||||
return m_dimension;
|
||||
}
|
||||
|
||||
Format GetFormatBase() const {
|
||||
return m_format;
|
||||
}
|
||||
|
||||
void SetValid(bool valid) {
|
||||
m_valid = valid;
|
||||
}
|
||||
|
||||
private:
|
||||
ResourceViewType m_viewType = ResourceViewType::ShaderResource;
|
||||
ResourceViewDimension m_dimension = ResourceViewDimension::Texture2D;
|
||||
Format m_format = Format::R8G8B8A8_UNorm;
|
||||
bool m_valid = true;
|
||||
};
|
||||
|
||||
class FakeShaderResourceView final : public XCEngine::RHI::RHIShaderResourceView {
|
||||
public:
|
||||
explicit FakeShaderResourceView(bool valid = true)
|
||||
: m_base(ResourceViewType::ShaderResource) {
|
||||
m_base.SetValid(valid);
|
||||
}
|
||||
|
||||
void Shutdown() override { m_base.ShutdownBase(); }
|
||||
void* GetNativeHandle() override { return m_base.GetNativeHandleBase(); }
|
||||
bool IsValid() const override { return m_base.IsValidBase(); }
|
||||
ResourceViewType GetViewType() const override { return m_base.GetViewTypeBase(); }
|
||||
ResourceViewDimension GetDimension() const override { return m_base.GetDimensionBase(); }
|
||||
Format GetFormat() const override { return m_base.GetFormatBase(); }
|
||||
|
||||
private:
|
||||
FakeResourceViewBase m_base;
|
||||
};
|
||||
|
||||
class FakeRenderTargetView final : public XCEngine::RHI::RHIRenderTargetView {
|
||||
public:
|
||||
FakeRenderTargetView()
|
||||
: m_base(ResourceViewType::RenderTarget) {
|
||||
}
|
||||
|
||||
void Shutdown() override { m_base.ShutdownBase(); }
|
||||
void* GetNativeHandle() override { return m_base.GetNativeHandleBase(); }
|
||||
bool IsValid() const override { return m_base.IsValidBase(); }
|
||||
ResourceViewType GetViewType() const override { return m_base.GetViewTypeBase(); }
|
||||
ResourceViewDimension GetDimension() const override { return m_base.GetDimensionBase(); }
|
||||
Format GetFormat() const override { return m_base.GetFormatBase(); }
|
||||
|
||||
private:
|
||||
FakeResourceViewBase m_base;
|
||||
};
|
||||
|
||||
class FakeVertexBufferView final : public XCEngine::RHI::RHIVertexBufferView {
|
||||
public:
|
||||
explicit FakeVertexBufferView(std::uint32_t size, std::uint32_t stride)
|
||||
: m_size(size)
|
||||
, m_stride(stride)
|
||||
, m_base(ResourceViewType::VertexBuffer, ResourceViewDimension::Buffer, Format::Unknown) {
|
||||
}
|
||||
|
||||
void Shutdown() override { m_base.ShutdownBase(); }
|
||||
void* GetNativeHandle() override { return m_base.GetNativeHandleBase(); }
|
||||
bool IsValid() const override { return m_base.IsValidBase(); }
|
||||
ResourceViewType GetViewType() const override { return m_base.GetViewTypeBase(); }
|
||||
ResourceViewDimension GetDimension() const override { return m_base.GetDimensionBase(); }
|
||||
Format GetFormat() const override { return m_base.GetFormatBase(); }
|
||||
std::uint64_t GetBufferAddress() const override { return 0u; }
|
||||
std::uint32_t GetSize() const override { return m_size; }
|
||||
std::uint32_t GetStride() const override { return m_stride; }
|
||||
|
||||
private:
|
||||
std::uint32_t m_size = 0;
|
||||
std::uint32_t m_stride = 0;
|
||||
FakeResourceViewBase m_base;
|
||||
};
|
||||
|
||||
class FakeBuffer final : public RHIBuffer {
|
||||
public:
|
||||
explicit FakeBuffer(const BufferDesc& desc)
|
||||
: m_size(desc.size)
|
||||
, m_stride(desc.stride)
|
||||
, m_type(static_cast<BufferType>(desc.bufferType))
|
||||
, m_storage(static_cast<std::size_t>(desc.size), 0u) {
|
||||
}
|
||||
|
||||
void* Map() override { return m_storage.data(); }
|
||||
void Unmap() override {}
|
||||
|
||||
void SetData(const void* data, size_t size, size_t offset = 0) override {
|
||||
if (data == nullptr || offset + size > m_storage.size()) {
|
||||
return;
|
||||
}
|
||||
std::memcpy(m_storage.data() + offset, data, size);
|
||||
}
|
||||
|
||||
std::uint64_t GetSize() const override { return m_size; }
|
||||
BufferType GetBufferType() const override { return m_type; }
|
||||
void SetBufferType(BufferType type) override { m_type = type; }
|
||||
std::uint32_t GetStride() const override { return m_stride; }
|
||||
void SetStride(std::uint32_t stride) override { m_stride = stride; }
|
||||
void* GetNativeHandle() override { return this; }
|
||||
ResourceStates GetState() const override { return m_state; }
|
||||
void SetState(ResourceStates state) override { m_state = state; }
|
||||
const std::string& GetName() const override { return m_name; }
|
||||
void SetName(const std::string& name) override { m_name = name; }
|
||||
void Shutdown() override { m_storage.clear(); }
|
||||
|
||||
private:
|
||||
std::uint64_t m_size = 0;
|
||||
std::uint32_t m_stride = 0;
|
||||
BufferType m_type = BufferType::Vertex;
|
||||
ResourceStates m_state = ResourceStates::Common;
|
||||
std::string m_name = {};
|
||||
std::vector<std::uint8_t> m_storage = {};
|
||||
};
|
||||
|
||||
class FakeDescriptorSet final : public RHIDescriptorSet {
|
||||
public:
|
||||
explicit FakeDescriptorSet(const DescriptorSetLayoutDesc& layout) {
|
||||
if (layout.bindings != nullptr && layout.bindingCount > 0u) {
|
||||
m_bindings.assign(layout.bindings, layout.bindings + layout.bindingCount);
|
||||
m_views.resize(layout.bindingCount, nullptr);
|
||||
m_samplers.resize(layout.bindingCount, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void Shutdown() override {
|
||||
m_views.clear();
|
||||
m_samplers.clear();
|
||||
m_constantBuffer.clear();
|
||||
m_constantDirty = false;
|
||||
}
|
||||
|
||||
void Bind() override {}
|
||||
void Unbind() override {}
|
||||
|
||||
void Update(std::uint32_t offset, RHIResourceView* view) override {
|
||||
if (offset >= m_views.size()) {
|
||||
m_views.resize(offset + 1u, nullptr);
|
||||
}
|
||||
m_views[offset] = view;
|
||||
}
|
||||
|
||||
void UpdateSampler(std::uint32_t offset, RHISampler* sampler) override {
|
||||
if (offset >= m_samplers.size()) {
|
||||
m_samplers.resize(offset + 1u, nullptr);
|
||||
}
|
||||
m_samplers[offset] = sampler;
|
||||
}
|
||||
|
||||
void WriteConstant(std::uint32_t, const void* data, size_t size, size_t offset = 0) override {
|
||||
if (data == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (m_constantBuffer.size() < offset + size) {
|
||||
m_constantBuffer.resize(offset + size, 0u);
|
||||
}
|
||||
std::memcpy(m_constantBuffer.data() + offset, data, size);
|
||||
m_constantDirty = true;
|
||||
}
|
||||
|
||||
std::uint32_t GetBindingCount() const override {
|
||||
return static_cast<std::uint32_t>(m_bindings.size());
|
||||
}
|
||||
|
||||
const DescriptorSetLayoutBinding* GetBindings() const override {
|
||||
return m_bindings.empty() ? nullptr : m_bindings.data();
|
||||
}
|
||||
|
||||
void* GetConstantBufferData() override {
|
||||
return m_constantBuffer.empty() ? nullptr : m_constantBuffer.data();
|
||||
}
|
||||
|
||||
size_t GetConstantBufferSize() const override {
|
||||
return m_constantBuffer.size();
|
||||
}
|
||||
|
||||
bool IsConstantDirty() const override {
|
||||
return m_constantDirty;
|
||||
}
|
||||
|
||||
void MarkConstantClean() override {
|
||||
m_constantDirty = false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<DescriptorSetLayoutBinding> m_bindings = {};
|
||||
std::vector<RHIResourceView*> m_views = {};
|
||||
std::vector<RHISampler*> m_samplers = {};
|
||||
std::vector<std::uint8_t> m_constantBuffer = {};
|
||||
bool m_constantDirty = false;
|
||||
};
|
||||
|
||||
class FakeDescriptorPool final : public RHIDescriptorPool {
|
||||
public:
|
||||
explicit FakeDescriptorPool(const DescriptorPoolDesc& desc)
|
||||
: m_desc(desc) {
|
||||
}
|
||||
|
||||
bool Initialize(const DescriptorPoolDesc& desc) override {
|
||||
m_desc = desc;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Shutdown() override {}
|
||||
void* GetNativeHandle() override { return this; }
|
||||
std::uint32_t GetDescriptorCount() const override { return m_desc.descriptorCount; }
|
||||
DescriptorHeapType GetType() const override { return m_desc.type; }
|
||||
|
||||
RHIDescriptorSet* AllocateSet(const DescriptorSetLayoutDesc& layout) override {
|
||||
++m_allocationCount;
|
||||
return new FakeDescriptorSet(layout);
|
||||
}
|
||||
|
||||
void FreeSet(RHIDescriptorSet* set) override {
|
||||
delete set;
|
||||
}
|
||||
|
||||
std::uint32_t GetAllocationCount() const {
|
||||
return m_allocationCount;
|
||||
}
|
||||
|
||||
private:
|
||||
DescriptorPoolDesc m_desc = {};
|
||||
std::uint32_t m_allocationCount = 0;
|
||||
};
|
||||
|
||||
class FakePipelineLayout final : public RHIPipelineLayout {
|
||||
public:
|
||||
bool Initialize(const XCEngine::RHI::RHIPipelineLayoutDesc& desc) override {
|
||||
m_desc = desc;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Shutdown() override {}
|
||||
void* GetNativeHandle() override { return this; }
|
||||
|
||||
private:
|
||||
XCEngine::RHI::RHIPipelineLayoutDesc m_desc = {};
|
||||
};
|
||||
|
||||
class FakePipelineState final : public RHIPipelineState {
|
||||
public:
|
||||
explicit FakePipelineState(const GraphicsPipelineDesc& desc)
|
||||
: m_inputLayout(desc.inputLayout)
|
||||
, m_rasterizer(desc.rasterizerState)
|
||||
, m_blend(desc.blendState)
|
||||
, m_depthStencil(desc.depthStencilState) {
|
||||
}
|
||||
|
||||
void SetInputLayout(const InputLayoutDesc& layout) override { m_inputLayout = layout; }
|
||||
void SetRasterizerState(const RasterizerDesc& state) override { m_rasterizer = state; }
|
||||
void SetBlendState(const BlendDesc& state) override { m_blend = state; }
|
||||
void SetDepthStencilState(const DepthStencilStateDesc& state) override { m_depthStencil = state; }
|
||||
void SetTopology(std::uint32_t topologyType) override { m_topologyType = topologyType; }
|
||||
void SetRenderTargetFormats(std::uint32_t, const std::uint32_t*, std::uint32_t) override {}
|
||||
void SetSampleCount(std::uint32_t count) override { m_sampleCount = count; }
|
||||
void SetComputeShader(RHIShader* shader) override { m_computeShader = shader; }
|
||||
const RasterizerDesc& GetRasterizerState() const override { return m_rasterizer; }
|
||||
const BlendDesc& GetBlendState() const override { return m_blend; }
|
||||
const DepthStencilStateDesc& GetDepthStencilState() const override { return m_depthStencil; }
|
||||
const InputLayoutDesc& GetInputLayout() const override { return m_inputLayout; }
|
||||
PipelineStateHash GetHash() const override { return {}; }
|
||||
RHIShader* GetComputeShader() const override { return m_computeShader; }
|
||||
bool HasComputeShader() const override { return m_computeShader != nullptr; }
|
||||
bool IsValid() const override { return true; }
|
||||
void EnsureValid() override {}
|
||||
void Shutdown() override {}
|
||||
void Bind() override {}
|
||||
void Unbind() override {}
|
||||
void* GetNativeHandle() override { return this; }
|
||||
PipelineType GetType() const override { return PipelineType::Graphics; }
|
||||
|
||||
private:
|
||||
InputLayoutDesc m_inputLayout = {};
|
||||
RasterizerDesc m_rasterizer = {};
|
||||
BlendDesc m_blend = {};
|
||||
DepthStencilStateDesc m_depthStencil = {};
|
||||
std::uint32_t m_topologyType = 0u;
|
||||
std::uint32_t m_sampleCount = 1u;
|
||||
RHIShader* m_computeShader = nullptr;
|
||||
};
|
||||
|
||||
class FakeSampler final : public RHISampler {
|
||||
public:
|
||||
void Shutdown() override {}
|
||||
void Bind(unsigned int) override {}
|
||||
void Unbind(unsigned int) override {}
|
||||
void* GetNativeHandle() override { return this; }
|
||||
unsigned int GetID() override { return 1u; }
|
||||
};
|
||||
|
||||
class FakeCommandQueue final : public RHICommandQueue {
|
||||
public:
|
||||
void Shutdown() override {}
|
||||
void ExecuteCommandLists(std::uint32_t, void**) override {}
|
||||
void Signal(XCEngine::RHI::RHIFence*, std::uint64_t) override {}
|
||||
void Wait(XCEngine::RHI::RHIFence*, std::uint64_t) override {}
|
||||
std::uint64_t GetCompletedValue() override { return 0u; }
|
||||
void WaitForIdle() override {}
|
||||
CommandQueueType GetType() const override { return CommandQueueType::Direct; }
|
||||
std::uint64_t GetTimestampFrequency() const override { return 0u; }
|
||||
void* GetNativeHandle() override { return this; }
|
||||
void WaitForPreviousFrame() override {}
|
||||
std::uint64_t GetCurrentFrame() const override { return 0u; }
|
||||
};
|
||||
|
||||
class FakeCommandList final : public RHICommandList {
|
||||
public:
|
||||
void Shutdown() override {}
|
||||
void Reset() override {}
|
||||
void Close() override {}
|
||||
void TransitionBarrier(RHIResourceView*, ResourceStates, ResourceStates) override {}
|
||||
void BeginRenderPass(XCEngine::RHI::RHIRenderPass*, XCEngine::RHI::RHIFramebuffer*, const Rect&, std::uint32_t, const XCEngine::RHI::ClearValue*) override {}
|
||||
void EndRenderPass() override {}
|
||||
void SetShader(RHIShader*) override {}
|
||||
|
||||
void SetPipelineState(RHIPipelineState* pso) override {
|
||||
pipelineStateHistory.push_back(pso);
|
||||
}
|
||||
|
||||
void SetGraphicsDescriptorSets(
|
||||
std::uint32_t,
|
||||
std::uint32_t count,
|
||||
RHIDescriptorSet** descriptorSets,
|
||||
RHIPipelineLayout*) override {
|
||||
if (count >= 2u && descriptorSets != nullptr) {
|
||||
textureDescriptorSetHistory.push_back(descriptorSets[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void SetComputeDescriptorSets(std::uint32_t, std::uint32_t, RHIDescriptorSet**, RHIPipelineLayout*) override {}
|
||||
void SetPrimitiveTopology(PrimitiveTopology topology) override { primitiveTopologyHistory.push_back(topology); }
|
||||
void SetViewport(const Viewport& viewport) override { viewportHistory.push_back(viewport); }
|
||||
void SetViewports(std::uint32_t count, const Viewport* viewports) override {
|
||||
for (std::uint32_t index = 0; index < count; ++index) {
|
||||
viewportHistory.push_back(viewports[index]);
|
||||
}
|
||||
}
|
||||
void SetScissorRect(const Rect& rect) override { scissorHistory.push_back(rect); }
|
||||
void SetScissorRects(std::uint32_t count, const Rect* rects) override {
|
||||
for (std::uint32_t index = 0; index < count; ++index) {
|
||||
scissorHistory.push_back(rects[index]);
|
||||
}
|
||||
}
|
||||
void SetRenderTargets(std::uint32_t count, RHIResourceView**, RHIResourceView* = nullptr) override {
|
||||
renderTargetBindHistory.push_back(count);
|
||||
}
|
||||
void SetStencilRef(std::uint8_t) override {}
|
||||
void SetBlendFactor(const float[4]) override {}
|
||||
void SetVertexBuffers(std::uint32_t, std::uint32_t, RHIResourceView**, const std::uint64_t*, const std::uint32_t*) override {}
|
||||
void SetIndexBuffer(RHIResourceView*, std::uint64_t) override {}
|
||||
|
||||
void Draw(std::uint32_t vertexCount, std::uint32_t, std::uint32_t startVertex, std::uint32_t) override {
|
||||
drawVertexCounts.push_back(vertexCount);
|
||||
drawStartVertices.push_back(startVertex);
|
||||
}
|
||||
|
||||
void DrawIndexed(std::uint32_t, std::uint32_t, std::uint32_t, std::int32_t, std::uint32_t) override {}
|
||||
void Clear(float, float, float, float, std::uint32_t) override {}
|
||||
void ClearRenderTarget(RHIResourceView*, const float[4], std::uint32_t = 0, const Rect* = nullptr) override {}
|
||||
void ClearDepthStencil(RHIResourceView*, float, std::uint8_t, std::uint32_t = 0, const Rect* = nullptr) override {}
|
||||
void CopyResource(RHIResourceView*, RHIResourceView*) override {}
|
||||
void Dispatch(std::uint32_t, std::uint32_t, std::uint32_t) override {}
|
||||
void* GetNativeHandle() override { return this; }
|
||||
|
||||
std::vector<RHIPipelineState*> pipelineStateHistory = {};
|
||||
std::vector<PrimitiveTopology> primitiveTopologyHistory = {};
|
||||
std::vector<Viewport> viewportHistory = {};
|
||||
std::vector<Rect> scissorHistory = {};
|
||||
std::vector<std::uint32_t> renderTargetBindHistory = {};
|
||||
std::vector<RHIDescriptorSet*> textureDescriptorSetHistory = {};
|
||||
std::vector<std::uint32_t> drawVertexCounts = {};
|
||||
std::vector<std::uint32_t> drawStartVertices = {};
|
||||
};
|
||||
|
||||
class FakeDevice final : public RHIDevice {
|
||||
public:
|
||||
bool Initialize(const XCEngine::RHI::RHIDeviceDesc&) override { return true; }
|
||||
void Shutdown() override {}
|
||||
RHIBuffer* CreateBuffer(const BufferDesc& desc) override { return new FakeBuffer(desc); }
|
||||
XCEngine::RHI::RHITexture* CreateTexture(const XCEngine::RHI::TextureDesc&) override { return nullptr; }
|
||||
XCEngine::RHI::RHITexture* CreateTexture(const XCEngine::RHI::TextureDesc&, const void*, size_t, std::uint32_t = 0) override { return nullptr; }
|
||||
XCEngine::RHI::RHISwapChain* CreateSwapChain(const XCEngine::RHI::SwapChainDesc&, RHICommandQueue*) override { return nullptr; }
|
||||
RHICommandList* CreateCommandList(const XCEngine::RHI::CommandListDesc&) override { return nullptr; }
|
||||
RHICommandQueue* CreateCommandQueue(const XCEngine::RHI::CommandQueueDesc&) override { return nullptr; }
|
||||
RHIShader* CreateShader(const XCEngine::RHI::ShaderCompileDesc&) override { return nullptr; }
|
||||
RHIPipelineState* CreatePipelineState(const GraphicsPipelineDesc& desc) override { return new FakePipelineState(desc); }
|
||||
RHIPipelineLayout* CreatePipelineLayout(const XCEngine::RHI::RHIPipelineLayoutDesc&) override { return new FakePipelineLayout(); }
|
||||
XCEngine::RHI::RHIFence* CreateFence(const XCEngine::RHI::FenceDesc&) override { return nullptr; }
|
||||
RHISampler* CreateSampler(const SamplerDesc&) override { return new FakeSampler(); }
|
||||
XCEngine::RHI::RHIRenderPass* CreateRenderPass(std::uint32_t, const XCEngine::RHI::AttachmentDesc*, const XCEngine::RHI::AttachmentDesc*) override { return nullptr; }
|
||||
XCEngine::RHI::RHIFramebuffer* CreateFramebuffer(XCEngine::RHI::RHIRenderPass*, std::uint32_t, std::uint32_t, std::uint32_t, RHIResourceView**, RHIResourceView*) override { return nullptr; }
|
||||
RHIDescriptorPool* CreateDescriptorPool(const DescriptorPoolDesc& desc) override { return new FakeDescriptorPool(desc); }
|
||||
RHIDescriptorSet* CreateDescriptorSet(RHIDescriptorPool*, const DescriptorSetLayoutDesc&) override { return nullptr; }
|
||||
RHIResourceView* CreateVertexBufferView(RHIBuffer* buffer, const ResourceViewDesc& desc) override {
|
||||
return new FakeVertexBufferView(static_cast<std::uint32_t>(buffer != nullptr ? buffer->GetSize() : 0u), desc.structureByteStride);
|
||||
}
|
||||
RHIResourceView* CreateIndexBufferView(RHIBuffer*, const ResourceViewDesc&) override { return nullptr; }
|
||||
RHIResourceView* CreateRenderTargetView(XCEngine::RHI::RHITexture*, const ResourceViewDesc&) override { return nullptr; }
|
||||
RHIResourceView* CreateDepthStencilView(XCEngine::RHI::RHITexture*, const ResourceViewDesc&) override { return nullptr; }
|
||||
RHIResourceView* CreateShaderResourceView(XCEngine::RHI::RHITexture*, const ResourceViewDesc&) override { return nullptr; }
|
||||
RHIResourceView* CreateUnorderedAccessView(XCEngine::RHI::RHITexture*, const ResourceViewDesc&) override { return nullptr; }
|
||||
const XCEngine::RHI::RHICapabilities& GetCapabilities() const override { return m_capabilities; }
|
||||
const XCEngine::RHI::RHIDeviceInfo& GetDeviceInfo() const override { return m_deviceInfo; }
|
||||
void* GetNativeDevice() override { return this; }
|
||||
|
||||
private:
|
||||
XCEngine::RHI::RHICapabilities m_capabilities = {};
|
||||
XCEngine::RHI::RHIDeviceInfo m_deviceInfo = {};
|
||||
};
|
||||
|
||||
UITextureHandle MakeExternalTextureHandle(FakeShaderResourceView& shaderView, std::uint32_t width = 32u, std::uint32_t height = 32u) {
|
||||
UITextureHandle texture = {};
|
||||
texture.nativeHandle = reinterpret_cast<std::uintptr_t>(&shaderView);
|
||||
texture.width = width;
|
||||
texture.height = height;
|
||||
texture.kind = UITextureHandleKind::ShaderResourceView;
|
||||
return texture;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(XCUIRHIRenderBackendTest, ExposesNativeOverlayEntryAndAtlasInjection) {
|
||||
static_assert(std::is_same_v<
|
||||
decltype(std::declval<XCUIRHIRenderBackend&>().Render(
|
||||
std::declval<const RenderContext&>(),
|
||||
std::declval<const RenderSurface&>(),
|
||||
std::declval<const UIDrawData&>())),
|
||||
bool>);
|
||||
static_assert(std::is_same_v<
|
||||
decltype(std::declval<XCUIRHIRenderBackend&>().SetTextAtlasProvider(
|
||||
std::declval<const IXCUITextAtlasProvider*>())) ,
|
||||
void>);
|
||||
static_assert(std::is_same_v<
|
||||
decltype(std::declval<const XCUIRHIRenderBackend&>().GetLastOverlayStats().drawListCount),
|
||||
std::size_t>);
|
||||
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
TEST(XCUIRHIRenderBackendTest, RenderRejectsInvalidContextAndLeavesStatsClear) {
|
||||
XCUIRHIRenderBackend backend = {};
|
||||
RenderSurface surface(128, 72);
|
||||
UIDrawData drawData = {};
|
||||
|
||||
EXPECT_FALSE(backend.Render(RenderContext(), surface, drawData));
|
||||
EXPECT_EQ(backend.GetLastOverlayStats().commandCount, 0u);
|
||||
EXPECT_EQ(backend.GetLastOverlayStats().renderedCommandCount, 0u);
|
||||
}
|
||||
|
||||
TEST(XCUIRHIRenderBackendTest, SetTextAtlasProviderStoresPointerAndResetStatsClearsCounters) {
|
||||
XCUIRHIRenderBackend backend = {};
|
||||
|
||||
class StubAtlasProvider final : public IXCUITextAtlasProvider {
|
||||
public:
|
||||
bool GetAtlasTextureView(PixelFormat, AtlasTextureView& outView) const override {
|
||||
outView = {};
|
||||
return false;
|
||||
}
|
||||
std::size_t GetFontCount() const override { return 0u; }
|
||||
FontHandle GetFont(std::size_t) const override { return {}; }
|
||||
FontHandle GetDefaultFont() const override { return {}; }
|
||||
bool GetFontInfo(FontHandle, FontInfo& outInfo) const override {
|
||||
outInfo = {};
|
||||
return false;
|
||||
}
|
||||
bool GetBakedFontInfo(FontHandle, float, BakedFontInfo& outInfo) const override {
|
||||
outInfo = {};
|
||||
return false;
|
||||
}
|
||||
bool FindGlyph(FontHandle, float, std::uint32_t, GlyphInfo& outInfo) const override {
|
||||
outInfo = {};
|
||||
return false;
|
||||
}
|
||||
} atlasProvider;
|
||||
|
||||
backend.SetTextAtlasProvider(&atlasProvider);
|
||||
EXPECT_EQ(backend.GetTextAtlasProvider(), &atlasProvider);
|
||||
|
||||
backend.SetTextAtlasProvider(nullptr);
|
||||
EXPECT_EQ(backend.GetTextAtlasProvider(), nullptr);
|
||||
|
||||
backend.ResetStats();
|
||||
EXPECT_EQ(backend.GetLastStats().drawListCount, 0u);
|
||||
EXPECT_EQ(backend.GetLastStats().commandCount, 0u);
|
||||
EXPECT_EQ(backend.GetLastStats().batchCount, 0u);
|
||||
EXPECT_EQ(backend.GetLastStats().renderedCommandCount, 0u);
|
||||
EXPECT_EQ(backend.GetLastStats().skippedCommandCount, 0u);
|
||||
EXPECT_EQ(backend.GetLastStats().textureResolveCount, 0u);
|
||||
EXPECT_EQ(backend.GetLastStats().textureCacheHitCount, 0u);
|
||||
}
|
||||
|
||||
TEST(XCUIRHIRenderBackendTest, RenderReusesExternalTextureBindingsAcrossTexturedBatches) {
|
||||
FakeDevice device = {};
|
||||
FakeCommandList commandList = {};
|
||||
FakeCommandQueue commandQueue = {};
|
||||
FakeRenderTargetView renderTarget = {};
|
||||
FakeShaderResourceView externalTextureView(true);
|
||||
|
||||
RenderContext context = {};
|
||||
context.device = &device;
|
||||
context.commandList = &commandList;
|
||||
context.commandQueue = &commandQueue;
|
||||
context.backendType = RHIType::D3D12;
|
||||
|
||||
RenderSurface surface(64, 48);
|
||||
surface.SetColorAttachment(&renderTarget);
|
||||
|
||||
UIDrawData drawData = {};
|
||||
UIDrawList& firstDrawList = drawData.EmplaceDrawList("FirstTexturedBatch");
|
||||
firstDrawList.PushClipRect(UIRect(4.0f, 6.0f, 12.0f, 10.0f));
|
||||
firstDrawList.AddImage(
|
||||
UIRect(0.0f, 0.0f, 18.0f, 18.0f),
|
||||
MakeExternalTextureHandle(externalTextureView),
|
||||
UIColor(1.0f, 0.8f, 0.6f, 1.0f));
|
||||
firstDrawList.PopClipRect();
|
||||
|
||||
UIDrawList& secondDrawList = drawData.EmplaceDrawList("SecondTexturedBatch");
|
||||
secondDrawList.PushClipRect(UIRect(20.0f, 8.0f, 10.0f, 12.0f));
|
||||
secondDrawList.AddImage(
|
||||
UIRect(18.0f, 4.0f, 16.0f, 16.0f),
|
||||
MakeExternalTextureHandle(externalTextureView),
|
||||
UIColor(0.7f, 0.9f, 1.0f, 1.0f));
|
||||
secondDrawList.PopClipRect();
|
||||
|
||||
XCUIRHIRenderBackend backend = {};
|
||||
ASSERT_TRUE(backend.Render(context, surface, drawData));
|
||||
|
||||
const XCUIRHIRenderBackend::OverlayStats& stats = backend.GetLastOverlayStats();
|
||||
EXPECT_EQ(stats.drawListCount, 2u);
|
||||
EXPECT_EQ(stats.commandCount, 6u);
|
||||
EXPECT_EQ(stats.batchCount, 2u);
|
||||
EXPECT_EQ(stats.colorBatchCount, 0u);
|
||||
EXPECT_EQ(stats.texturedBatchCount, 2u);
|
||||
EXPECT_EQ(stats.scissoredBatchCount, 2u);
|
||||
EXPECT_EQ(stats.renderedCommandCount, 2u);
|
||||
EXPECT_EQ(stats.skippedCommandCount, 0u);
|
||||
EXPECT_EQ(stats.textureResolveCount, 2u);
|
||||
EXPECT_EQ(stats.textureCacheHitCount, 1u);
|
||||
EXPECT_EQ(stats.vertexCount, 12u);
|
||||
EXPECT_EQ(stats.triangleCount, 4u);
|
||||
|
||||
ASSERT_EQ(commandList.renderTargetBindHistory.size(), 1u);
|
||||
ASSERT_EQ(commandList.viewportHistory.size(), 1u);
|
||||
ASSERT_EQ(commandList.drawVertexCounts.size(), 2u);
|
||||
EXPECT_EQ(commandList.drawVertexCounts[0], 6u);
|
||||
EXPECT_EQ(commandList.drawVertexCounts[1], 6u);
|
||||
ASSERT_EQ(commandList.textureDescriptorSetHistory.size(), 2u);
|
||||
EXPECT_EQ(commandList.textureDescriptorSetHistory[0], commandList.textureDescriptorSetHistory[1]);
|
||||
|
||||
ASSERT_EQ(commandList.scissorHistory.size(), 4u);
|
||||
EXPECT_EQ(commandList.scissorHistory[0].left, 0);
|
||||
EXPECT_EQ(commandList.scissorHistory[0].top, 0);
|
||||
EXPECT_EQ(commandList.scissorHistory[0].right, 64);
|
||||
EXPECT_EQ(commandList.scissorHistory[0].bottom, 48);
|
||||
EXPECT_EQ(commandList.scissorHistory[1].left, 4);
|
||||
EXPECT_EQ(commandList.scissorHistory[1].top, 6);
|
||||
EXPECT_EQ(commandList.scissorHistory[1].right, 16);
|
||||
EXPECT_EQ(commandList.scissorHistory[1].bottom, 16);
|
||||
EXPECT_EQ(commandList.scissorHistory[2].left, 20);
|
||||
EXPECT_EQ(commandList.scissorHistory[2].top, 8);
|
||||
EXPECT_EQ(commandList.scissorHistory[2].right, 30);
|
||||
EXPECT_EQ(commandList.scissorHistory[2].bottom, 20);
|
||||
EXPECT_EQ(commandList.scissorHistory[3].left, 0);
|
||||
EXPECT_EQ(commandList.scissorHistory[3].top, 0);
|
||||
EXPECT_EQ(commandList.scissorHistory[3].right, 64);
|
||||
EXPECT_EQ(commandList.scissorHistory[3].bottom, 48);
|
||||
}
|
||||
|
||||
TEST(XCUIRHIRenderBackendTest, RenderSkipsTexturedBatchWhenExternalTextureViewIsInvalid) {
|
||||
FakeDevice device = {};
|
||||
FakeCommandList commandList = {};
|
||||
FakeCommandQueue commandQueue = {};
|
||||
FakeRenderTargetView renderTarget = {};
|
||||
FakeShaderResourceView invalidTextureView(false);
|
||||
|
||||
RenderContext context = {};
|
||||
context.device = &device;
|
||||
context.commandList = &commandList;
|
||||
context.commandQueue = &commandQueue;
|
||||
context.backendType = RHIType::D3D12;
|
||||
|
||||
RenderSurface surface(32, 32);
|
||||
surface.SetColorAttachment(&renderTarget);
|
||||
|
||||
UIDrawData drawData = {};
|
||||
UIDrawList& drawList = drawData.EmplaceDrawList("InvalidExternalTexture");
|
||||
drawList.AddImage(
|
||||
UIRect(2.0f, 4.0f, 12.0f, 8.0f),
|
||||
MakeExternalTextureHandle(invalidTextureView),
|
||||
UIColor(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
|
||||
XCUIRHIRenderBackend backend = {};
|
||||
ASSERT_TRUE(backend.Render(context, surface, drawData));
|
||||
|
||||
const XCUIRHIRenderBackend::OverlayStats& stats = backend.GetLastOverlayStats();
|
||||
EXPECT_EQ(stats.commandCount, 1u);
|
||||
EXPECT_EQ(stats.batchCount, 1u);
|
||||
EXPECT_EQ(stats.texturedBatchCount, 1u);
|
||||
EXPECT_EQ(stats.renderedCommandCount, 0u);
|
||||
EXPECT_EQ(stats.skippedCommandCount, 1u);
|
||||
EXPECT_EQ(stats.textureResolveCount, 1u);
|
||||
EXPECT_EQ(stats.textureCacheHitCount, 0u);
|
||||
EXPECT_EQ(commandList.drawVertexCounts.size(), 0u);
|
||||
EXPECT_EQ(commandList.textureDescriptorSetHistory.size(), 0u);
|
||||
}
|
||||
45
tests/NewEditor/test_xcui_standalone_text_atlas_provider.cpp
Normal file
45
tests/NewEditor/test_xcui_standalone_text_atlas_provider.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "XCUIBackend/XCUIStandaloneTextAtlasProvider.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::Editor::XCUIBackend::IXCUITextAtlasProvider;
|
||||
using XCEngine::Editor::XCUIBackend::XCUIStandaloneTextAtlasProvider;
|
||||
|
||||
TEST(XCUIStandaloneTextAtlasProviderTest, BuildsDefaultEditorAtlasWithoutImGuiContext) {
|
||||
XCUIStandaloneTextAtlasProvider provider = {};
|
||||
|
||||
IXCUITextAtlasProvider::AtlasTextureView atlasView = {};
|
||||
ASSERT_TRUE(provider.IsReady());
|
||||
ASSERT_TRUE(provider.GetAtlasTextureView(IXCUITextAtlasProvider::PixelFormat::RGBA32, atlasView));
|
||||
EXPECT_TRUE(atlasView.IsValid());
|
||||
EXPECT_EQ(atlasView.format, IXCUITextAtlasProvider::PixelFormat::RGBA32);
|
||||
EXPECT_GT(provider.GetFontCount(), 0u);
|
||||
}
|
||||
|
||||
TEST(XCUIStandaloneTextAtlasProviderTest, ExposesDefaultFontMetricsAndGlyphs) {
|
||||
XCUIStandaloneTextAtlasProvider provider = {};
|
||||
const IXCUITextAtlasProvider::FontHandle defaultFont = provider.GetDefaultFont();
|
||||
ASSERT_TRUE(defaultFont.IsValid());
|
||||
|
||||
IXCUITextAtlasProvider::FontInfo fontInfo = {};
|
||||
ASSERT_TRUE(provider.GetFontInfo(defaultFont, fontInfo));
|
||||
EXPECT_GT(fontInfo.nominalSize, 0.0f);
|
||||
|
||||
IXCUITextAtlasProvider::BakedFontInfo bakedFontInfo = {};
|
||||
ASSERT_TRUE(provider.GetBakedFontInfo(defaultFont, 0.0f, bakedFontInfo));
|
||||
EXPECT_GT(bakedFontInfo.lineHeight, 0.0f);
|
||||
EXPECT_GT(bakedFontInfo.rasterizerDensity, 0.0f);
|
||||
|
||||
IXCUITextAtlasProvider::GlyphInfo glyphInfo = {};
|
||||
ASSERT_TRUE(provider.FindGlyph(defaultFont, 0.0f, static_cast<std::uint32_t>('A'), glyphInfo));
|
||||
EXPECT_EQ(glyphInfo.requestedCodepoint, static_cast<std::uint32_t>('A'));
|
||||
EXPECT_GT(glyphInfo.advanceX, 0.0f);
|
||||
EXPECT_GE(glyphInfo.u1, glyphInfo.u0);
|
||||
EXPECT_GE(glyphInfo.v1, glyphInfo.v0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user