From 40dce19b0eb6d3f0befe7177df070f021bf41f07 Mon Sep 17 00:00:00 2001 From: akallabeth Date: Thu, 20 Feb 2025 11:39:18 +0100 Subject: [PATCH] [codec,test] add new unit test TestFreeRDPCodecCursor * Generate a series of cursor images from source dumps and compare these to a reference image * Generation is automatic by picking up .c/.h files from libfreerdp/codec/test/cursor directory. --- libfreerdp/codec/test/CMakeLists.txt | 37 ++++++++- .../codec/test/TestFreeRDPCodecCursor.c | 79 +++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 libfreerdp/codec/test/TestFreeRDPCodecCursor.c diff --git a/libfreerdp/codec/test/CMakeLists.txt b/libfreerdp/codec/test/CMakeLists.txt index eb9f1ff7f..b51fd7ed8 100644 --- a/libfreerdp/codec/test/CMakeLists.txt +++ b/libfreerdp/codec/test/CMakeLists.txt @@ -10,6 +10,7 @@ set(TESTS TestFreeRDPCodecZGfx.c TestFreeRDPCodecPlanar.c TestFreeRDPCodecCopy.c + TestFreeRDPCodecCursor.c TestFreeRDPCodecClear.c TestFreeRDPCodecInterleaved.c TestFreeRDPCodecProgressive.c @@ -24,11 +25,45 @@ if(BUILD_TESTING_INTERNAL) list(APPEND TESTS TestFreeRDPCodecMppc.c TestFreeRDPCodecNCrush.c TestFreeRDPCodecXCrush.c) endif() +file(GLOB CURSOR_TESTCASES_C LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "cursor/*.c") +file(GLOB CURSOR_TESTCASES_H LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "cursor/*.h") + +# Create a combined header for all testcases +set(TESTCASE_HEADER "${CMAKE_CURRENT_BINARY_DIR}/testcases.h") +write_file(${TESTCASE_HEADER} "#pragma once\n") +foreach(header ${CURSOR_TESTCASES_H}) + write_file(${TESTCASE_HEADER} "#include <${header}>" APPEND) +endforeach() + +write_file(${TESTCASE_HEADER} "\nstatic const gdiPalette* testcase_palette[] = {" APPEND) +foreach(header ${CURSOR_TESTCASES_H}) + get_filename_component(NAME ${header} NAME_WE) + write_file(${TESTCASE_HEADER} "&${NAME}_palette," APPEND) +endforeach() +write_file(${TESTCASE_HEADER} "};\n" APPEND) + +write_file(${TESTCASE_HEADER} "static const rdpPointer* testcase_pointer[] = {" APPEND) +foreach(header ${CURSOR_TESTCASES_H}) + get_filename_component(NAME ${header} NAME_WE) + write_file(${TESTCASE_HEADER} "&${NAME}_pointer," APPEND) +endforeach() +write_file(${TESTCASE_HEADER} "};\n" APPEND) + +write_file(${TESTCASE_HEADER} "static const uint8_t* testcase_image_bgra32[] = {" APPEND) +foreach(header ${CURSOR_TESTCASES_H}) + get_filename_component(NAME ${header} NAME_WE) + write_file(${TESTCASE_HEADER} "${NAME}_image_bgra32," APPEND) +endforeach() +write_file(${TESTCASE_HEADER} "};" APPEND) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + create_test_sourcelist(SRCS ${DRIVER} ${TESTS}) add_compile_definitions(CMAKE_CURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") add_compile_definitions(CMAKE_CURRENT_BINARY_DIR="${CMAKE_CURRENT_BINARY_DIR}") -add_executable(${MODULE_NAME} ${SRCS}) +add_executable(${MODULE_NAME} ${SRCS} ${CURSOR_TESTCASES_H} ${CURSOR_TESTCASES_C} ${TESTCASE_HEADER}) target_link_libraries(${MODULE_NAME} freerdp winpr) diff --git a/libfreerdp/codec/test/TestFreeRDPCodecCursor.c b/libfreerdp/codec/test/TestFreeRDPCodecCursor.c new file mode 100644 index 000000000..88435b89f --- /dev/null +++ b/libfreerdp/codec/test/TestFreeRDPCodecCursor.c @@ -0,0 +1,79 @@ +#include + +#include +#include +#include + +#include "testcases.h" + +static BOOL run_testcase(size_t x, const gdiPalette* palette, const rdpPointer* pointer, + const uint8_t* ref) +{ + WINPR_ASSERT(palette); + WINPR_ASSERT(pointer); + WINPR_ASSERT(ref); + + WLog_INFO("test", "running cursor test case %" PRIuz, x); + BOOL rc = FALSE; + const uint32_t format = PIXEL_FORMAT_BGRA32; + const size_t width = pointer->width; + const size_t height = pointer->height; + const size_t xorBpp = pointer->xorBpp; + const size_t bpp = FreeRDPGetBytesPerPixel(format); + const size_t stride = width * bpp; + + uint8_t* bmp = calloc(stride, height); + if (!bmp) + goto fail; + + const BOOL result = freerdp_image_copy_from_pointer_data( + bmp, format, 0, 0, 0, width, height, pointer->xorMaskData, pointer->lengthXorMask, + pointer->andMaskData, pointer->lengthAndMask, xorBpp, palette); + if (!result) + goto fail; + + rc = TRUE; + for (size_t y = 0; y < height; y++) + { + const uint8_t* linea = &bmp[y * stride]; + const uint8_t* lineb = &ref[y * stride]; + for (size_t x = 0; x < stride; x++) + { + const uint8_t a = linea[x]; + const uint8_t b = lineb[x]; + if (a != b) + { + printf("xxx: %" PRIuz "x%" PRIuz ": color %" PRIuz " diff 0x%02" PRIx8 + "<-->0x%02" PRIx8 "\n", + x / 4, y, x % 4, a, b); + rc = FALSE; + } + } + } + +fail: + free(bmp); + WLog_INFO("test", "cursor test case %" PRIuz ": %s", x, rc ? "success" : "failure"); + return rc; +} + +int TestFreeRDPCodecCursor(WINPR_ATTR_UNUSED int argc, WINPR_ATTR_UNUSED char* argv[]) +{ + const size_t palette_len = ARRAYSIZE(testcase_palette); + const size_t pointer_len = ARRAYSIZE(testcase_pointer); + const size_t bmp_len = ARRAYSIZE(testcase_image_bgra32); + WINPR_ASSERT(palette_len == pointer_len); + WINPR_ASSERT(palette_len == bmp_len); + + int rc = 0; + for (size_t x = 0; x < palette_len; x++) + { + const rdpPalette* palette = testcase_palette[x]; + const rdpPointer* pointer = testcase_pointer[x]; + const uint8_t* bmp = testcase_image_bgra32[x]; + + if (!run_testcase(x, palette, pointer, bmp)) + rc = -1; + } + return rc; +}