UI Testing

Overview

The Test module provides functions to emulate clicks, key presses, encoder turns, time passing, and compare the UI with reference images.

These functions can be easily used in any test framework (such as Unity, GoogleTest, etc.), and assertions can be performed to check if, for example:

  • A widget's value is different from the expected value after emulating user inputs.

  • The values are incorrect after some time has passed.

  • The screen's content is different from the reference image.

  • Some event functions are not triggered.

  • Etc.

Note that it is assumed the tests are performed on a desktop or server environment, where there are no memory constraints.

Usage

The Test module can be enabled by LV_USE_TEST, and it consists of the following components:

  • Helpers

  • Display emulation

  • Input device emulation

  • Screenshot comparison

Helpers

Time

To emulate elapsed time, two functions can be used:

  1. lv_test_wait(ms): Emulates that ms milliseconds have elapsed, but it also calls lv_timer_handler after each millisecond. This is useful to check if events (e.g., long press, long press repeat) and timers were triggered correctly over time.

  2. lv_test_fast_forward(ms): Jumps ms milliseconds ahead and calls lv_timer_handler only once at the end.

lv_refr_now(NULL) is called at the end of both functions to ensure that animations and widget coordinates are recalculated.

lv_refr_now(NULL) can also be called manually to force LVGL to refresh the emulated display.

Memory Usage

If LV_USE_STDLIB_MALLOC is set to LV_STDLIB_BUILTIN, memory usage and memory leaks can be monitored.

size_t mem1 = lv_test_get_free_mem();
<create and delete items>
size_t mem2 = lv_test_get_free_mem();
if(mem1 != mem2) fail();

It might make sense to create and delete items in a loop many times and add a small tolerance to the memory leakage test. This might be needed due to potential memory fragmentation. Empirically, a tolerance of 32 bytes is recommended.

if(LV_ABS((int64_t)mem2 - (int64_t)mem1) > 32) fail();

Display Emulation

By calling lv_test_display_create(hor_res, ver_res), a dummy display can be created.

It functions like any other normal display, but its content exists only in memory.

When creating this display, the horizontal and vertical resolutions must be passed. Internally, a framebuffer will be allocated for this size, and XRGB8888 color format will be set.

The resolution and color format can be changed at any time by calling lv_display_set_resolution and lv_display_set_color_format.

Input Device Emulation

By calling lv_test_indev_create_all, three test input devices will be created:

  1. A pointer (for touch or mouse)

  2. A keypad

  3. An encoder

For example, this is how a scroll gesture can be emulated:

lv_test_mouse_move_to(20, 30);
lv_test_mouse_press();
lv_test_wait(20);
lv_test_mouse_move_by(0, 100);
lv_test_wait(20);
lv_test_mouse_release();
lv_test_wait(20);

It is recommended to add lv_test_wait after user actions to ensure that the new state and coordinates are read and applied from the input device.

After that, the user can check if the given widget was really scrolled by getting the Y coordinate of a child.

int32_t y_start = lv_obj_get_y(child);
<scroll emulation>
int32_t y_end = lv_obj_get_y(child);
if(y_start + 100 != y_end) fail();

Please refer to lv_test_indev.h for the list of supported input device emulation functions.

Screenshot Comparison

bool lv_test_screenshot_compare(const char * fn_ref) is a useful function to compare the content of the emulated display with reference PNG images.

The screenshot comparison uses libpng, so it needs to be linked to LVGL when this feature is required. To avoid making the entire Test module dependent on libpng, screenshot comparison can be individually enabled by LV_USE_TEST_SCREENSHOT_COMPARE.

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 comparison fails, an <image_name>_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.

The reference PNG images should have a 32-bit color format and match the display size.

The test display's content will be converted to XRGB8888 to simplify comparison with the reference images. The conversion is supported from the following formats (i.e., the test display should have a color format from any of these):

To read and decode PNG images and to store the converted rendered image, a few MBs of RAM are dynamically allocated using the standard malloc (not lv_malloc).

API

lv_test_display.h

lv_test_indev.h

lv_test_screenshot_compare.h

lv_test_helpers.h