diff --git a/docs/src/debugging/test.rst b/docs/src/debugging/test.rst index 58e8cdb41c..766ba557d3 100644 --- a/docs/src/debugging/test.rst +++ b/docs/src/debugging/test.rst @@ -129,14 +129,15 @@ for the list of supported input device emulation functions. Screenshot Comparison --------------------- -``bool lv_test_screenshot_compare(const char * fn_ref)`` is a useful function +``lv_test_screenshot_result_t lv_test_screenshot_compare(const char * fn_ref)`` is a useful function to compare the content of the emulated display with reference PNG images. This function works in a practical way: - If the folder(s) referenced in ``fn_ref`` do not exist, they will be created automatically. -- If the reference image is not found, it will be created automatically from the rendered screen. +- If the reference image is not found, it will be created automatically from the rendered screen + (unless ``LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE`` is ``0``). - If the comparison fails, an ``_err.png`` file will be created with the rendered content next to the reference image. - If the comparison fails, the X and Y coordinates of the first divergent pixel, along with the actual and expected colors, will also be printed. @@ -160,14 +161,13 @@ To read and decode PNG images and to store the converted rendered image, a few M (not :cpp:expr:`lv_malloc`). -The screenshot comparison uses ``lodepng`` which is built-in to LVGL and just needs to be ebnabled with +The screenshot comparison uses ``lodepng`` which is built-in to LVGL and just needs to be enabled with ``LV_USE_LODEPNG``. To avoid making the entire Test module dependent on ``lodepng``, screenshot comparison can be individually enabled by ``LV_USE_TEST_SCREENSHOT_COMPARE``. - .. _test_api: API diff --git a/lv_conf_template.h b/lv_conf_template.h index fc91e0b5c6..f1913dc3df 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -1204,6 +1204,12 @@ /** Enable `lv_test_screenshot_compare`. * Requires lodepng and a few MB of extra RAM. */ #define LV_USE_TEST_SCREENSHOT_COMPARE 0 + +#if LV_USE_TEST_SCREENSHOT_COMPARE + /** 1: Automatically create missing reference images*/ + #define LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE 1 +#endif /*LV_USE_TEST_SCREENSHOT_COMPARE*/ + #endif /*LV_USE_TEST*/ /** Enable loading XML UIs runtime */ diff --git a/src/debugging/test/lv_test_screenshot_compare.c b/src/debugging/test/lv_test_screenshot_compare.c index ae0929de67..60ae0add06 100644 --- a/src/debugging/test/lv_test_screenshot_compare.c +++ b/src/debugging/test/lv_test_screenshot_compare.c @@ -56,7 +56,7 @@ /********************** * STATIC PROTOTYPES **********************/ -static bool screenshot_compare(const char * fn_ref, uint8_t tolerance); +static lv_test_screenshot_result_t screenshot_compare(const char * fn_ref, uint8_t tolerance); static unsigned read_png_file(lv_draw_buf_t ** refr_draw_buf, unsigned * width, unsigned * height, const char * file_name); static unsigned write_png_file(void * raw_img, uint32_t width, uint32_t height, char * file_name); @@ -75,18 +75,16 @@ static void create_folders_if_needed(const char * path) ; * GLOBAL FUNCTIONS **********************/ -bool lv_test_screenshot_compare(const char * fn_ref) +lv_test_screenshot_result_t lv_test_screenshot_compare(const char * fn_ref) { - bool pass; lv_obj_t * scr = lv_screen_active(); lv_obj_invalidate(scr); lv_refr_now(NULL); - pass = screenshot_compare(fn_ref, REF_IMG_TOLERANCE); - if(!pass) return false; - - return true; + lv_test_screenshot_result_t res; + res = screenshot_compare(fn_ref, REF_IMG_TOLERANCE); + return res; } /********************** @@ -96,14 +94,16 @@ bool lv_test_screenshot_compare(const char * fn_ref) /** * Compare the content of the frame buffer with a reference image * @param fn_ref reference image path - * @return true: test passed; false: test failed + * @return An element of lv_test_screenshot_result_t */ -static bool screenshot_compare(const char * fn_ref, uint8_t tolerance) +static lv_test_screenshot_result_t screenshot_compare(const char * fn_ref, uint8_t tolerance) { char fn_ref_full[256]; lv_snprintf(fn_ref_full, sizeof(fn_ref_full), "%s%s", REF_IMGS_PATH, fn_ref); +#if LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE create_folders_if_needed(fn_ref_full); +#endif lv_draw_buf_t * draw_buf = lv_display_get_buf_active(NULL); @@ -115,18 +115,24 @@ static bool screenshot_compare(const char * fn_ref, uint8_t tolerance) unsigned ref_img_height = 0; unsigned res = read_png_file(&ref_draw_buf, &ref_img_width, &ref_img_height, fn_ref_full); if(res) { + lv_test_screenshot_result_t comp_res; +#if LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE LV_LOG_WARN("%s%s", fn_ref_full, " was not found, creating it now from the rendered screen"); write_png_file(screen_buf_xrgb8888, draw_buf->header.w, draw_buf->header.h, fn_ref_full); + comp_res = LV_TEST_SCREENSHOT_RESULT_PASSED; +#else + comp_res = LV_TEST_SCREENSHOT_RESULT_NO_REFERENCE_IMAGE; +#endif lv_free(screen_buf_xrgb8888); if(ref_draw_buf) lv_draw_buf_destroy(ref_draw_buf); - return true; + return comp_res; } if(ref_img_width != draw_buf->header.w || ref_img_height != draw_buf->header.h) { LV_LOG_WARN("The dimensions of the rendered and the %s reference image don't match", fn_ref); lv_free(screen_buf_xrgb8888); if(ref_draw_buf) lv_draw_buf_destroy(ref_draw_buf); - return false; + return LV_TEST_SCREENSHOT_RESULT_FAILED; } @@ -173,7 +179,7 @@ static bool screenshot_compare(const char * fn_ref, uint8_t tolerance) fflush(stdout); lv_free(screen_buf_xrgb8888); if(ref_draw_buf) lv_draw_buf_destroy(ref_draw_buf); - return !err; + return err ? LV_TEST_SCREENSHOT_RESULT_FAILED : LV_TEST_SCREENSHOT_RESULT_PASSED; } diff --git a/src/debugging/test/lv_test_screenshot_compare.h b/src/debugging/test/lv_test_screenshot_compare.h index b37fe3ee3e..6790027ee7 100644 --- a/src/debugging/test/lv_test_screenshot_compare.h +++ b/src/debugging/test/lv_test_screenshot_compare.h @@ -24,6 +24,30 @@ extern "C" { * TYPEDEFS **********************/ +/** + * Return value of `lv_test_screenshot_compare` + */ +typedef enum { + /** + * The screenshot is different than the reference image + */ + LV_TEST_SCREENSHOT_RESULT_FAILED, + + /** + * The screenshot is the same as the reference image. + * It is also returned if `LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE` is enabled + * and the reference image was missing. + */ + LV_TEST_SCREENSHOT_RESULT_PASSED, + + /** + * If `LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE` is not enabled + * and the reference image is missing. + */ + LV_TEST_SCREENSHOT_RESULT_NO_REFERENCE_IMAGE, + +} lv_test_screenshot_result_t; + /********************** * GLOBAL PROTOTYPES **********************/ @@ -33,14 +57,14 @@ extern "C" { * - If the reference image is not found it will be created automatically from the rendered screen. * - If the compare fails an `_err.png` file will be created with the rendered content next to the reference image. * - * It requires libPNG. + * It requires lodepng. * - * @param fn_ref path to the reference image. Will be appended toREF_IMGS_PATH if set. - * @return true: the reference image and the display are the same; false: they are different (`_err.png` is created). + * @param fn_ref path to the reference image. Will be appended to REF_IMGS_PATH if set. + * @return An element of `lv_test_screenshot_result_t` * @note This function assumes that the default display is the test display that was created by * `lv_test_display_create()` */ -bool lv_test_screenshot_compare(const char * fn_ref); +lv_test_screenshot_result_t lv_test_screenshot_compare(const char * fn_ref); /********************** * MACROS diff --git a/src/lv_api_map_v9_4.h b/src/lv_api_map_v9_4.h index 031012dc0b..cadc2b2f20 100644 --- a/src/lv_api_map_v9_4.h +++ b/src/lv_api_map_v9_4.h @@ -33,6 +33,8 @@ extern "C" { #define lv_tabview_rename_tab lv_tabview_set_tab_text #define lv_wayland_timer_handler lv_timer_handler #define lv_wayland_display_close_f_t lv_wayland_display_close_cb_t +#define lv_xml_component_unregister lv_xml_unregister_component +#define lv_xml_test_unregister lv_xml_unregister_test #ifdef __cplusplus } /*extern "C"*/ diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index c1748434bb..7b590ada23 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -3823,6 +3823,22 @@ #define LV_USE_TEST_SCREENSHOT_COMPARE 0 #endif #endif + +#if LV_USE_TEST_SCREENSHOT_COMPARE + /** 1: Automatically create missing reference images*/ + #ifndef LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE + #ifdef LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE + #define LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE CONFIG_LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE + #else + #define LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE 0 + #endif + #else + #define LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE 1 + #endif + #endif +#endif /*LV_USE_TEST_SCREENSHOT_COMPARE*/ + #endif /*LV_USE_TEST*/ /** Enable loading XML UIs runtime */ diff --git a/src/xml/lv_xml.c b/src/xml/lv_xml.c index eb2d72d977..e9f7aa031b 100644 --- a/src/xml/lv_xml.c +++ b/src/xml/lv_xml.c @@ -258,7 +258,7 @@ void lv_xml_init(void) void lv_xml_deinit(void) { #if LV_USE_TEST - lv_xml_test_unregister(); + lv_xml_unregister_test(); #endif lv_xml_load_deinit(); diff --git a/src/xml/lv_xml_component.c b/src/xml/lv_xml_component.c index 88bc44d58a..e518a4af08 100644 --- a/src/xml/lv_xml_component.c +++ b/src/xml/lv_xml_component.c @@ -190,7 +190,7 @@ lv_result_t lv_xml_register_component_from_data(const char * name, const char * if(!scope->view_def) { LV_LOG_WARN("Failed to extract view content"); /* Clean up and return error */ - lv_xml_component_unregister(name); + lv_xml_unregister_component(name); return LV_RESULT_INVALID; } } @@ -256,7 +256,7 @@ lv_result_t lv_xml_register_component_from_file(const char * path) return res; } -lv_result_t lv_xml_component_unregister(const char * name) +lv_result_t lv_xml_unregister_component(const char * name) { lv_xml_component_scope_t * scope = lv_xml_component_get_scope(name); if(scope == NULL) return LV_RESULT_INVALID; diff --git a/src/xml/lv_xml_component.h b/src/xml/lv_xml_component.h index 04be0debed..8bc5c37a4e 100644 --- a/src/xml/lv_xml_component.h +++ b/src/xml/lv_xml_component.h @@ -61,7 +61,7 @@ lv_xml_component_scope_t * lv_xml_component_get_scope(const char * component_nam * @param name the name of the component (used during registration) * @return LV_RESULT_OK on successful unregistration, LV_RESULT_INVALID otherwise. */ -lv_result_t lv_xml_component_unregister(const char * name); +lv_result_t lv_xml_unregister_component(const char * name); /********************** * MACROS diff --git a/src/xml/lv_xml_test.c b/src/xml/lv_xml_test.c index cf62be84f9..2d7bd22096 100644 --- a/src/xml/lv_xml_test.c +++ b/src/xml/lv_xml_test.c @@ -98,7 +98,7 @@ static lv_tick_get_cb_t tick_cb_original; lv_result_t lv_xml_test_register_from_data(const char * xml_def, const char * ref_image_path_prefix) { /*Cleanup the previous test*/ - lv_xml_test_unregister(); + lv_xml_unregister_test(); test.ref_image_path_prefix = ref_image_path_prefix; @@ -181,7 +181,7 @@ lv_result_t lv_xml_test_register_from_file(const char * path, const char * ref_i return res; } -void lv_xml_test_unregister(void) +void lv_xml_unregister_test(void) { uint32_t i; for(i = 0; i < test.step_cnt; i++) { @@ -206,7 +206,7 @@ void lv_xml_test_unregister(void) test.steps = NULL; test.step_cnt = 0; - lv_xml_component_unregister(LV_TEST_NAME); + lv_xml_unregister_component(LV_TEST_NAME); } void lv_xml_test_run_init(void) @@ -249,14 +249,43 @@ bool lv_xml_test_run_next(uint32_t slowdown) return passed; } -void lv_xml_test_run_stop(void) +bool lv_xml_test_run_to(uint32_t slowdown, uint32_t step) { - lv_obj_delete(cursor); - lv_tick_set_cb(tick_cb_original); - lv_display_delete(test_display); - lv_test_indev_delete_all(); + if(test.step_cnt == 0) { + LV_LOG_WARN("There are no test steps"); + return false; + } + + if(step >= test.step_cnt) { + + LV_LOG_WARN("%" LV_PRIu32 " step index was greater than the total step count." + "Limited to %" LV_PRIu32, + step, test.step_cnt); + step = test.step_cnt - 1; + } + + bool passed = true; + while(passed && test.step_act <= step) { + passed = lv_xml_test_run_next(slowdown); + } + + return passed; } +void lv_xml_test_run_stop(void) +{ + if(cursor) { + lv_obj_delete(cursor); + cursor = NULL; + } + if(test_display) { + lv_tick_set_cb(tick_cb_original); + lv_display_delete(test_display); + test_display = NULL; + } + + lv_test_indev_delete_all(); +} uint32_t lv_xml_test_run_all(uint32_t slowdown) { @@ -280,6 +309,11 @@ uint32_t lv_xml_test_get_step_count(void) return test.step_cnt; } +uint32_t lv_xml_test_get_step_next(void) +{ + return test.step_act; +} + lv_xml_test_step_type_t lv_xml_test_get_step_type(uint32_t idx) { if(idx >= test.step_cnt) return LV_XML_TEST_STEP_TYPE_NONE; @@ -363,9 +397,14 @@ static bool execute_step(lv_xml_test_step_t * step, uint32_t slowdown) lv_obj_invalidate(lv_display_get_layer_sys(test_display)); /*Do the actual screenshot compare*/ - res = lv_test_screenshot_compare(step->param.screenshot_compare.path); - if(!res) { + lv_test_screenshot_result_t screen_cmp_res; + screen_cmp_res = lv_test_screenshot_compare(step->param.screenshot_compare.path); + if(screen_cmp_res != LV_TEST_SCREENSHOT_RESULT_PASSED) { LV_LOG_WARN("screenshot compare of `%s` failed", step->param.screenshot_compare.path); + res = false; + } + else { + res = true; } /*Restore*/ diff --git a/src/xml/lv_xml_test.h b/src/xml/lv_xml_test.h index d51b671397..c18178b6a5 100644 --- a/src/xml/lv_xml_test.h +++ b/src/xml/lv_xml_test.h @@ -58,7 +58,7 @@ lv_result_t lv_xml_test_register_from_file(const char * path, const char * ref_i /** * Free resources allocated for testing. */ -void lv_xml_test_unregister(void); +void lv_xml_unregister_test(void); /** * Switch to testing mode. Needs to be called to use `lv_xml_test_run_next()` @@ -68,10 +68,19 @@ void lv_xml_test_run_init(void); /** * Run the next test step. * @param slowdown 0: max speed, 1: real speed, 2: half speed, ... ,10: ten times slower - * @return true: the step passed; false: the step failed + * @return true: the step passed; false: the step failed */ bool lv_xml_test_run_next(uint32_t slowdown); +/** + * Run the test until a given step, like running until a breakpoint + * @param slowdown 0: max speed, 1: real speed, 2: half speed, ... ,10: ten times slower + * @param step the last step to execute + * @return true: all steps were passed; + * false: a step failed, use `lv_xml_test_get_step_next` to get which step failed + */ +bool lv_xml_test_run_to(uint32_t slowdown, uint32_t step); + /** * Leave testing mode. */ @@ -92,6 +101,13 @@ uint32_t lv_xml_test_run_all(uint32_t slowdown); */ uint32_t lv_xml_test_get_step_count(void); +/** + * Get the next test step's index. + * @return the index of next step + */ +uint32_t lv_xml_test_get_step_next(void); + + /** * Get the type of a step * @param idx the index of a step (< step_count) diff --git a/tests/src/test_cases/xml/test_xml_style.c b/tests/src/test_cases/xml/test_xml_style.c index 7803c90658..e5be5c853c 100644 --- a/tests/src/test_cases/xml/test_xml_style.c +++ b/tests/src/test_cases/xml/test_xml_style.c @@ -179,7 +179,7 @@ void test_xml_style_remove(void) /*s2 is added and removed so the red bg_color shouldn't be applied*/ TEST_ASSERT_NOT_EQUAL_COLOR(lv_color_hex(0xff0000), lv_obj_get_style_bg_color(obj, LV_PART_KNOB)); - lv_xml_component_unregister("comp1"); + lv_xml_unregister_component("comp1"); } void test_xml_style_binding(void) @@ -224,8 +224,7 @@ void test_xml_style_prop_binding(void) ""; lv_xml_register_component_from_data("comp1", comp1_xml); - lv_xml_component_unregister("comp1"); - return; + lv_obj_t * obj = lv_xml_create(lv_screen_active(), "comp1", NULL); lv_obj_add_state(obj, LV_STATE_CHECKED);