diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 8f6bab37e..f7d83abd3 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v2 - uses: ammaraskar/gcc-problem-matcher@master - name: Run tests - run: sudo apt-get install libpng-dev ruby-full; cd tests; python ./main.py report + run: sudo apt-get install libpng-dev ruby-full gcovr; cd tests; python ./main.py report - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 if: github.event_name == 'push' diff --git a/.gitignore b/.gitignore index 06df6e1a6..6a8878be6 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ out_html __pycache__ /emscripten_builder test_screenshot_error.h +build/ +tests/build_*/ +tests/report/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d2f3cdf4..cdc1e40f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,7 +68,10 @@ endif() # CONFIG_LVGL else() -file(GLOB_RECURSE SOURCES src/*.c) +file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/*.c) add_library(lvgl STATIC ${SOURCES}) +file(GLOB_RECURSE EXAMPLE_SOURCES ${CMAKE_CURRENT_LIST_DIR}/examples/*.c) +add_library(lvgl_examples STATIC ${EXAMPLE_SOURCES}) + endif() diff --git a/tests/.gitignore b/tests/.gitignore index 1fce10c0e..d484e9e15 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,3 +1,2 @@ *.out *_Runner.c -/report \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..92efe0c77 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,294 @@ +if(ESP_PLATFORM) + +################################### +# Tests do not build for ESP-IDF. # +################################### + +else() + +cmake_minimum_required(VERSION 3.13) +project(lvgl_tests LANGUAGES C) + +set(LVGL_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +set(LVGL_TEST_OPTIONS_MINIMAL_MONOCHROME + -DLV_COLOR_DEPTH=1 + -DLV_MEM_SIZE=65535 + -DLV_DPI_DEF=40 + -DLV_DRAW_COMPLEX=0 + -DLV_USE_METER=0 + -DLV_USE_LOG=1 + -DLV_USE_ASSERT_NULL=0 + -DLV_USE_ASSERT_MALLOC=0 + -DLV_USE_ASSERT_MEM_INTEGRITY=0 + -DLV_USE_ASSERT_OBJ=0 + -DLV_USE_ASSERT_STYLE=0 + -DLV_USE_USER_DATA=0 + -DLV_FONT_UNSCII_8=1 + -DLV_USE_BIDI=0 + -DLV_USE_ARABIC_PERSIAN_CHARS=0 + -DLV_BUILD_EXAMPLES=1 + -DLV_FONT_DEFAULT=&lv_font_montserrat_14 +) + +set(LVGL_TEST_OPTIONS_NORMAL_8BIT + -DLV_COLOR_DEPTH=8 + -DLV_MEM_SIZE=65535 + -DLV_DPI_DEF=40 + -DLV_DRAW_COMPLEX=1 + -DLV_USE_LOG=1 + -DLV_USE_ASSERT_NULL=0 + -DLV_USE_ASSERT_MALLOC=0 + -DLV_USE_ASSERT_MEM_INTEGRITY=0 + -DLV_USE_ASSERT_OBJ=0 + -DLV_USE_ASSERT_STYLE=0 + -DLV_USE_USER_DATA=0 + -DLV_FONT_UNSCII_8=1 + -DLV_USE_FONT_SUBPX=1 + -DLV_USE_BIDI=0 + -DLV_USE_ARABIC_PERSIAN_CHARS=0 + -DLV_BUILD_EXAMPLES=1 + -DLV_FONT_DEFAULT=&lv_font_montserrat_14 +) + +set(LVGL_TEST_OPTIONS_16BIT + -DLV_COLOR_DEPTH=16 + -DLV_COLOR_16_SWAP=0 + -DLV_MEM_SIZE=65536 + -DLV_DPI_DEF=40 + -DLV_DRAW_COMPLEX=1 + -DLV_USE_LOG=1 + -DLV_USE_ASSERT_NULL=0 + -DLV_USE_ASSERT_MALLOC=0 + -DLV_USE_ASSERT_MEM_INTEGRITY=0 + -DLV_USE_ASSERT_OBJ=0 + -DLV_USE_ASSERT_STYLE=0 + -DLV_USE_USER_DATA=0 + -DLV_FONT_UNSCII_8=1 + -DLV_USE_FONT_SUBPX=1 + -DLV_USE_BIDI=0 + -DLV_USE_ARABIC_PERSIAN_CHARS=0 + -DLV_BUILD_EXAMPLES=1 + -DLV_FONT_DEFAULT=&lv_font_montserrat_14 +) + +set(LVGL_TEST_OPTIONS_16BIT_SWAP + -DLV_COLOR_DEPTH=16 + -DLV_COLOR_16_SWAP=1 + -DLV_MEM_SIZE=65536 + -DLV_DPI_DEF=40 + -DLV_DRAW_COMPLEX=1 + -DLV_USE_LOG=1 + -DLV_USE_ASSERT_NULL=0 + -DLV_USE_ASSERT_MALLOC=0 + -DLV_USE_ASSERT_MEM_INTEGRITY=0 + -DLV_USE_ASSERT_OBJ=0 + -DLV_USE_ASSERT_STYLE=0 + -DLV_USE_USER_DATA=0 + -DLV_FONT_UNSCII_8=1 + -DLV_USE_FONT_SUBPX=1 + -DLV_USE_BIDI=0 + -DLV_USE_ARABIC_PERSIAN_CHARS=0 + -DLV_BUILD_EXAMPLES=1 + -DLV_FONT_DEFAULT=&lv_font_montserrat_14 +) + +set(LVGL_TEST_OPTIONS_FULL_32BIT + -DLV_COLOR_DEPTH=32 + -DLV_MEM_SIZE=8388608 + -DLV_DPI_DEF=160 + -DLV_DRAW_COMPLEX=1 + -DLV_SHADOW_CACHE_SIZE=1 + -DLV_IMG_CACHE_DEF_SIZE=32 + -DLV_USE_LOG=1 + -DLV_USE_LOG_LEVEL=LV_LOG_LEVEL_TRACE + -DLV_LOG_PRINTF=1 + -DLV_USE_FONT_SUBPX=1 + -DLV_FONT_SUBPX_BGR=1 + -DLV_USE_PERF_MONITOR=1 + -DLV_USE_ASSERT_NULL=1 + -DLV_USE_ASSERT_MALLOC=1 + -DLV_USE_ASSERT_MEM_INTEGRITY=1 + -DLV_USE_ASSERT_OBJ=1 + -DLV_USE_ASSERT_STYLE=1 + -DLV_USE_USER_DATA=1 + -DLV_USE_LARGE_COORD=1 + -DLV_FONT_MONTSERRAT_8=1 + -DLV_FONT_MONTSERRAT_10=1 + -DLV_FONT_MONTSERRAT_12=1 + -DLV_FONT_MONTSERRAT_14=1 + -DLV_FONT_MONTSERRAT_16=1 + -DLV_FONT_MONTSERRAT_18=1 + -DLV_FONT_MONTSERRAT_20=1 + -DLV_FONT_MONTSERRAT_22=1 + -DLV_FONT_MONTSERRAT_24=1 + -DLV_FONT_MONTSERRAT_26=1 + -DLV_FONT_MONTSERRAT_28=1 + -DLV_FONT_MONTSERRAT_30=1 + -DLV_FONT_MONTSERRAT_32=1 + -DLV_FONT_MONTSERRAT_34=1 + -DLV_FONT_MONTSERRAT_36=1 + -DLV_FONT_MONTSERRAT_38=1 + -DLV_FONT_MONTSERRAT_40=1 + -DLV_FONT_MONTSERRAT_42=1 + -DLV_FONT_MONTSERRAT_44=1 + -DLV_FONT_MONTSERRAT_46=1 + -DLV_FONT_MONTSERRAT_48=1 + -DLV_FONT_MONTSERRAT_12_SUBPX=1 + -DLV_FONT_MONTSERRAT_28_COMPRESSED=1 + -DLV_FONT_DEJAVU_16_PERSIAN_HEBREW=1 + -DLV_FONT_SIMSUN_16_CJK=1 + -DLV_FONT_UNSCII_8=1 + -DLV_FONT_UNSCII_16=1 + -DLV_FONT_FMT_TXT_LARGE=1 + -DLV_USE_FONT_COMPRESSED=1 + -DLV_USE_BIDI=1 + -DLV_USE_ARABIC_PERSIAN_CHARS=1 + -DLV_USE_PERF_MONITOR=1 + -DLV_USE_MEM_MONITOR=1 + -DLV_LABEL_TEXT_SELECTION=1 + -DLV_BUILD_EXAMPLES=1 + -DLV_FONT_DEFAULT=&lv_font_montserrat_24 + ) + + set(LVGL_TEST_OPTIONS_TEST + --coverage + -DLV_COLOR_DEPTH=32 + -DLV_MEM_SIZE=2097152 + -DLV_SHADOW_CACHE_SIZE=10240 + -DLV_IMG_CACHE_DEF_SIZE=32 + -DLV_USE_LOG=1 + -DLV_LOG_PRINTF=1 + -DLV_USE_FONT_SUBPX=1 + -DLV_FONT_SUBPX_BGR=1 + -DLV_USE_ASSERT_NULL=0 + -DLV_USE_ASSERT_MALLOC=0 + -DLV_USE_ASSERT_MEM_INTEGRITY=0 + -DLV_USE_ASSERT_OBJ=0 + -DLV_USE_ASSERT_STYLE=0 + -DLV_USE_USER_DATA=1 + -DLV_USE_LARGE_COORD=1 + -DLV_FONT_MONTSERRAT_14=1 + -DLV_FONT_MONTSERRAT_16=1 + -DLV_FONT_MONTSERRAT_18=1 + -DLV_FONT_MONTSERRAT_24=1 + -DLV_FONT_MONTSERRAT_48=1 + -DLV_FONT_MONTSERRAT_12_SUBPX=1 + -DLV_FONT_MONTSERRAT_28_COMPRESSED=1 + -DLV_FONT_DEJAVU_16_PERSIAN_HEBREW=1 + -DLV_FONT_SIMSUN_16_CJK=1 + -DLV_FONT_UNSCII_8=1 + -DLV_FONT_UNSCII_16=1 + -DLV_FONT_FMT_TXT_LARGE=1 + -DLV_USE_FONT_COMPRESSED=1 + -DLV_USE_BIDI=1 + -DLV_USE_ARABIC_PERSIAN_CHARS=1 + -DLV_LABEL_TEXT_SELECTION=1 + -DLV_BUILD_EXAMPLES=1 + -DLV_FONT_DEFAULT=&lv_font_montserrat_14 +) + +if (OPTIONS_MINIMAL_MONOCHROME) + set (TEST_OPTIONS ${LVGL_TEST_OPTIONS_MINIMAL_MONOCHROME}) +elseif (OPTIONS_NORMAL_8BIT) + set (TEST_OPTIONS ${LVGL_TEST_OPTIONS_NORMAL_8BIT}) +elseif (OPTIONS_16BIT) + set (TEST_OPTIONS ${LVGL_TEST_OPTIONS_16BIT}) +elseif (OPTIONS_16BIT_SWAP) + set (TEST_OPTIONS ${LVGL_TEST_OPTIONS_16BIT_SWAP}) +elseif (OPTIONS_FULL_32BIT) + set (TEST_OPTIONS ${LVGL_TEST_OPTIONS_FULL_32BIT}) +elseif (OPTIONS_TEST) + set (TEST_OPTIONS ${LVGL_TEST_OPTIONS_TEST}) + set (TEST_LIBS --coverage) +else() + message(FATAL_ERROR "Must provide an options value.") +endif() + +# Options lvgl and all test files are compiled with. +set(COMPILE_OPTIONS + -DLV_CONF_PATH=${LVGL_TEST_DIR}/src/lv_test_conf.h + -DLV_BUILD_TEST + -pedantic-errors + -Wall + -Wclobbered + -Wdeprecated + -Wdouble-promotion + -Wempty-body + -Werror + -Wextra + -Wformat-security + -Wmaybe-uninitialized + -Wmissing-prototypes + -Wpointer-arith + -Wmultichar + -Wno-discarded-qualifiers + -Wpedantic + -Wreturn-type + -Wshadow + -Wshift-negative-value + -Wsizeof-pointer-memaccess + -Wstack-usage=2048 + -Wtype-limits + -Wundef + -Wuninitialized + -Wunreachable-code + ${TEST_OPTIONS} +) + +get_filename_component(LVGL_DIR ${LVGL_TEST_DIR} DIRECTORY) + +# Include lvgl project file. +include(${LVGL_DIR}/CMakeLists.txt) +target_compile_options(lvgl PUBLIC ${COMPILE_OPTIONS}) +target_compile_options(lvgl_examples PUBLIC ${COMPILE_OPTIONS}) + + +set(TEST_INCLUDE_DIRS + $ + $ + $ +) + +add_library(test_common + STATIC + src/lv_test_indev.c + src/lv_test_init.c + src/test_fonts/font_1.c + src/test_fonts/font_2.c + src/test_fonts/font_3.c + unity/unity_support.c + unity/unity.c +) +target_include_directories(test_common PUBLIC ${TEST_INCLUDE_DIRS}) +target_compile_options(test_common PUBLIC ${COMPILE_OPTIONS}) + +# Some examples `#include "lvgl/lvgl.h"` - which is a path which is not +# in this source repository. If this repo is in a directory names 'lvgl' +# then we can add our parent directory to the include path. +# TODO: This is not good practice and should be fixed. +get_filename_component(LVGL_PARENT_DIR ${LVGL_DIR} DIRECTORY) +target_include_directories(lvgl_examples PUBLIC $) + +# Generate one test executable for each source file pair. +# The sources in src/test_runners is auto-generated, the +# sources in src/test_cases is the actual test case. +file( GLOB TEST_CASE_FILES src/test_cases/*.c ) +foreach( test_case_fname ${TEST_CASE_FILES} ) + # If test file is foo/bar/baz.c then test_name is "baz". + get_filename_component(test_name ${test_case_fname} NAME_WLE) + if (${test_name} STREQUAL "_test_template") + continue() + endif() + # Create path to auto-generated source file. + set(test_runner_fname src/test_runners/${test_name}_Runner.c) + add_executable( ${test_name} + ${test_case_fname} + ${test_runner_fname} + ) + target_link_libraries(${test_name} test_common lvgl_examples lvgl png ${TEST_LIBS}) + target_include_directories(${test_name} PUBLIC ${TEST_INCLUDE_DIRS}) + target_compile_options(${test_name} PUBLIC ${COMPILE_OPTIONS}) +endforeach( test_case_fname ${TEST_CASE_FILES} ) + +endif() diff --git a/tests/Makefile b/tests/Makefile deleted file mode 100644 index 1ecc96aa5..000000000 --- a/tests/Makefile +++ /dev/null @@ -1,56 +0,0 @@ -# -# Makefile -# -CC ?= gcc -LVGL_DIR ?= ${shell pwd}/../.. -LVGL_DIR_NAME ?= lvgl - -WARNINGS = -Werror -Wall -Wextra \ - -Wshadow -Wundef -Wmaybe-uninitialized -Wmissing-prototypes -Wpointer-arith -Wuninitialized \ - -Wunreachable-code -Wreturn-type -Wmultichar -Wformat-security -Wdouble-promotion -Wclobbered -Wdeprecated \ - -Wempty-body -Wshift-negative-value -Wstack-usage=2048 \ - -Wtype-limits -Wsizeof-pointer-memaccess -Wmissing-prototypes -Wno-discarded-qualifiers - -WARNINGS += -Wpedantic -pedantic-errors - -#-Wno-unused-value -Wno-unused-parameter -OPTIMIZATION ?= -g0 - -CFLAGS ?= -I$(LVGL_DIR)/ --coverage -Isrc -Iunity $(DEFINES) $(WARNINGS) $(OPTIMIZATION) -I$(LVGL_DIR) -I. - -LDFLAGS ?= -lpng --coverage -BIN ?= test - -include ../lvgl.mk - -CSRCS += ${TEST_SRC} -CSRCS += src/lv_test_init.c -CSRCS += src/lv_test_indev.c -CSRCS := $(CSRCS) $(EXTRA_CSRCS) - -OBJEXT ?= .o - -AOBJS = $(ASRCS:.S=$(OBJEXT)) -COBJS = $(CSRCS:.c=$(OBJEXT)) - -MAINOBJ = $(MAINSRC:.c=$(OBJEXT)) - -SRCS = $(ASRCS) $(CSRCS) $(MAINSRC) -OBJS = $(AOBJS) $(COBJS) - -## MAINOBJ -> OBJFILES - -all: default - -%.o: %.c - @$(CC) $(CFLAGS) -c -o $@ $< -# @echo "CC $<" - -default: $(AOBJS) $(COBJS) $(MAINOBJ) - $(CC) -o $(BIN) $(MAINOBJ) $(AOBJS) $(COBJS) $(LDFLAGS) - -clean: - find ../ -type f -name '*.o' -exec rm -f {} + - find ../ -type f -name '*.gcda' -exec rm -f {} + - find ../ -type f -name '*.gcno' -exec rm -f {} + - rm -f $(BIN) diff --git a/tests/README.md b/tests/README.md index 56c9223b9..8481a3522 100644 --- a/tests/README.md +++ b/tests/README.md @@ -5,9 +5,11 @@ The tests in the folder can be run locally and automatically by GitHub CI. ## Running locally ### Requirements (Linux) -1. Be sure GCC and Python3 is installed. -2. Install [gcovr](https://gcovr.com/en/stable/index.html) with `pip install gcovr` -3. Install Ruby with `sudo apt-get install ruby-full` +1. Be sure GCC and Python3 are installed. +2. Install [cmake](https://cmake.org/) with `sudo apt install cmake`. +3. Install [gcovr](https://gcovr.com/en/stable/index.html) with `sudo apt install gcovr` +4. Install Ruby with `sudo apt install ruby-full` +5. Install libpng development package with `sudo apt install libpng-dev` ### Run test 1. Enter `lvgl/tests/` @@ -18,11 +20,10 @@ The tests in the folder can be run locally and automatically by GitHub CI. For example: - `./main.py` Run all the test as they run in the CI. -- `./main.py report test noclean` Run only the test, should be sued when writing tests. +- `./main.py report test noclean` Run only the test, should be used when writing tests. ## Running automatically -TODO GitHub's CI automatically runs these tests on pushes and pull requests to `master` and `releasev8.*` branches. diff --git a/tests/build.py b/tests/build.py deleted file mode 100755 index 9275c0016..000000000 --- a/tests/build.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python3 - -import os -import re -import subprocess - -test_dir = os.path.dirname(os.path.realpath(__file__)) -lvgl_dir = os.path.abspath(os.path.join(test_dir, os.pardir)) -lvgl_dir_name = 'lvgl' # LVGL subdirectory (base name) in lvgl_dir. -lvgl_parent_dir = os.path.abspath(os.path.join(lvgl_dir, os.pardir)) - -lv_conf_path = os.path.join(lvgl_dir, 'tests/src/lv_test_conf.h') -base_defines = ['-DLV_CONF_PATH="%s"' % lv_conf_path, '-DLV_BUILD_TEST'] - -def maybe_quote(obj): - if type(obj) == str: - return '"%s"' % obj - return obj - - -def zip_defines(defines): - return ['-D%s=%s' % (key, maybe_quote(defines[key])) for key in defines] - - -def build(defines): - global base_defines - optimization = ['-O3', '-g0'] - d_all = base_defines + zip_defines(defines) - - cmd_env = os.environ.copy() - cmd_env['BIN'] = 'test.bin' - cmd_env['MAINSRC'] = 'src/lv_test_main.c' - cmd_env['LVGL_DIR'] = lvgl_parent_dir - cmd_env['LVGL_DIR_NAME'] = lvgl_dir_name - cmd_env['DEFINES'] = ' '.join(d_all) - cmd_env['OPTIMIZATION'] = ' '.join(optimization) - - print("") - print("Build") - print("-----------------------", flush=True) - # -s makes it silence - subprocess.check_call(['make', '-s', '--jobs=%d' % os.cpu_count()], env=cmd_env) - - print("") - print("Run") - print("-----------------------", flush=True) - subprocess.check_call("./test.bin") - - -def build_test(defines, test_name): - global base_defines - optimization = ['-g0'] - - print("") - print("") - print("~~~~~~~~~~~~~~~~~~~~~~~~") - print(re.search("/[a-z_]*$", test_name).group(0)[1:]) - print("~~~~~~~~~~~~~~~~~~~~~~~~", flush=True) - - d_all = base_defines + zip_defines(defines) - - test_file_name = test_name + ".c" - test_file_runner_name = test_name + "_Runner.c" - test_file_runner_name = test_file_runner_name.replace( - "/test_cases/", "/test_runners/") - cmd_env = os.environ.copy() - cmd_env['BIN'] = 'test.bin' - cmd_env['MAINSRC'] = test_file_name - cmd_env['TEST_SRC'] = test_file_runner_name - cmd_env['LVGL_DIR'] = lvgl_parent_dir - cmd_env['LVGL_DIR_NAME'] = lvgl_dir_name - cmd_env['DEFINES'] = ' '.join(d_all) - cmd_env['OPTIMIZATION'] = ' '.join(optimization) - extra_csrcs = [ - 'unity/unity.c', 'unity/unity_support.c', - 'src/test_fonts/font_1.c', 'src/test_fonts/font_2.c', - 'src/test_fonts/font_3.c' - ] - cmd_env['EXTRA_CSRCS'] = ' '.join(extra_csrcs) - - print("") - print("Build") - print("-----------------------", flush=True) - # -s makes it silence - subprocess.check_call(['make', '-s', '--jobs=%d' % os.cpu_count()], env=cmd_env) - - print("") - print("Run") - print("-----------------------") - subprocess.check_call('./test.bin') - - -def clean(): - print("") - print("Clean") - print("-----------------------", flush=True) - - cmd_env = os.environ.copy() - cmd_env['LVGL_DIR'] = lvgl_parent_dir - cmd_env['LVGL_DIR_NAME'] = lvgl_dir_name - subprocess.check_call(['make', 'clean'], env=cmd_env) - try: - os.remove('test.bin') - except FileNotFoundError: - pass diff --git a/tests/defines.py b/tests/defines.py deleted file mode 100644 index a34a0a741..000000000 --- a/tests/defines.py +++ /dev/null @@ -1,231 +0,0 @@ - -minimal_monochrome = { - "LV_COLOR_DEPTH":1, - "LV_MEM_SIZE":64 * 1024, - - "LV_DPI_DEF":40, - "LV_DRAW_COMPLEX":0, - "LV_USE_METER":0, - - "LV_USE_LOG":1, - - "LV_USE_ASSERT_NULL":0, - "LV_USE_ASSERT_MALLOC":0, - "LV_USE_ASSERT_MEM_INTEGRITY":0, - "LV_USE_ASSERT_OBJ":0, - "LV_USE_ASSERT_STYLE":0, - - "LV_USE_USER_DATA": 0, - - "LV_FONT_UNSCII_8":1, - - "LV_USE_BIDI": 0, - "LV_USE_ARABIC_PERSIAN_CHARS":0, - - "LV_BUILD_EXAMPLES":1, - - "LV_FONT_DEFAULT":"&lv_font_montserrat_14", - -} - -normal_8bit = { - "LV_COLOR_DEPTH":8, - - "LV_MEM_SIZE":64 * 1024, - - "LV_DPI_DEF":40, - "LV_DRAW_COMPLEX":1, - - "LV_USE_LOG":1, - - "LV_USE_ASSERT_NULL":0, - "LV_USE_ASSERT_MALLOC":0, - "LV_USE_ASSERT_MEM_INTEGRITY":0, - "LV_USE_ASSERT_OBJ":0, - "LV_USE_ASSERT_STYLE":0, - - "LV_USE_USER_DATA": 0, - - "LV_FONT_UNSCII_8":1, - - "LV_USE_FONT_SUBPX": 1, - "LV_USE_BIDI": 0, - "LV_USE_ARABIC_PERSIAN_CHARS":0, - - "LV_BUILD_EXAMPLES":1, - - "LV_FONT_DEFAULT":"&lv_font_montserrat_14", -} - - -minimal_16bit = { - "LV_COLOR_DEPTH":16, - "LV_MEM_CUSTOM":1, - - "LV_DPI_DEF":40, - "LV_DRAW_COMPLEX":0, - "LV_USE_METER":0, - - "LV_USE_LOG":1, - - "LV_USE_ASSERT_NULL":0, - "LV_USE_ASSERT_MALLOC":0, - "LV_USE_ASSERT_MEM_INTEGRITY":0, - "LV_USE_ASSERT_OBJ":0, - "LV_USE_ASSERT_STYLE":0, - - "LV_USE_USER_DATA": 0, - - "LV_FONT_UNSCII_8":1, - - "LV_USE_BIDI": 0, - "LV_USE_ARABIC_PERSIAN_CHARS":0, - - "LV_BUILD_EXAMPLES":1, - - "LV_FONT_DEFAULT":"&lv_font_montserrat_14", - -} - -normal_16bit_swap = { - "LV_COLOR_DEPTH":16, - "LV_COLOR_16_SWAP":1, - - "LV_MEM_SIZE":64 * 1024, - - "LV_DPI_DEF":40, - "LV_DRAW_COMPLEX":1, - - "LV_USE_LOG":1, - - "LV_USE_ASSERT_NULL":0, - "LV_USE_ASSERT_MALLOC":0, - "LV_USE_ASSERT_MEM_INTEGRITY":0, - "LV_USE_ASSERT_OBJ":0, - "LV_USE_ASSERT_STYLE":0, - - "LV_USE_USER_DATA": 0, - - "LV_FONT_UNSCII_8":1, - - "LV_USE_FONT_SUBPX": 1, - "LV_USE_BIDI": 0, - "LV_USE_ARABIC_PERSIAN_CHARS":0, - - "LV_BUILD_EXAMPLES":1, - - "LV_FONT_DEFAULT":"&lv_font_montserrat_14", -} - -full_32bit = { - "LV_COLOR_DEPTH":32, - "LV_MEM_SIZE":8 * 1024 * 1024, - - "LV_DPI_DEF":160, - "LV_DRAW_COMPLEX":1, - "LV_SHADOW_CACHE_SIZE":1, - "LV_IMG_CACHE_DEF_SIZE":32, - - "LV_USE_LOG":1, - "LV_USE_LOG_LEVEL":"LV_LOG_LEVEL_TRACE", - "LV_LOG_PRINTF":1, - "LV_USE_FONT_SUBPX": 1, - "LV_FONT_SUBPX_BGR":1, - "LV_USE_PERF_MONITOR":1, - - "LV_USE_ASSERT_NULL":1, - "LV_USE_ASSERT_MALLOC":1, - "LV_USE_ASSERT_MEM_INTEGRITY":1, - "LV_USE_ASSERT_OBJ":1, - "LV_USE_ASSERT_STYLE":1, - - "LV_USE_USER_DATA": 1, - - "LV_USE_LARGE_COORD": 1, - - "LV_FONT_MONTSERRAT_8":1, - "LV_FONT_MONTSERRAT_10":1, - "LV_FONT_MONTSERRAT_12":1, - "LV_FONT_MONTSERRAT_14":1, - "LV_FONT_MONTSERRAT_16":1, - "LV_FONT_MONTSERRAT_18":1, - "LV_FONT_MONTSERRAT_20":1, - "LV_FONT_MONTSERRAT_22":1, - "LV_FONT_MONTSERRAT_24":1, - "LV_FONT_MONTSERRAT_26":1, - "LV_FONT_MONTSERRAT_28":1, - "LV_FONT_MONTSERRAT_30":1, - "LV_FONT_MONTSERRAT_32":1, - "LV_FONT_MONTSERRAT_34":1, - "LV_FONT_MONTSERRAT_36":1, - "LV_FONT_MONTSERRAT_38":1, - "LV_FONT_MONTSERRAT_40":1, - "LV_FONT_MONTSERRAT_42":1, - "LV_FONT_MONTSERRAT_44":1, - "LV_FONT_MONTSERRAT_46":1, - "LV_FONT_MONTSERRAT_48":1, - "LV_FONT_MONTSERRAT_12_SUBPX":1, - "LV_FONT_MONTSERRAT_28_COMPRESSED":1, - "LV_FONT_DEJAVU_16_PERSIAN_HEBREW":1, - "LV_FONT_SIMSUN_16_CJK":1, - "LV_FONT_UNSCII_8":1, - "LV_FONT_UNSCII_16":1, - "LV_FONT_FMT_TXT_LARGE":1, - "LV_USE_FONT_COMPRESSED":1, - - "LV_USE_BIDI": 1, - "LV_USE_ARABIC_PERSIAN_CHARS":1, - "LV_USE_PERF_MONITOR":1, - "LV_USE_MEM_MONITOR":1, - - "LV_LABEL_TEXT_SELECTION":1, - - "LV_BUILD_EXAMPLES":1, - - "LV_FONT_DEFAULT":"&lv_font_montserrat_24", -} - -test = { - "LV_COLOR_DEPTH":32, - "LV_MEM_SIZE":2 * 1024 * 1024, - - "LV_SHADOW_CACHE_SIZE":10*1024, - "LV_IMG_CACHE_DEF_SIZE":32, - - "LV_USE_LOG":1, - "LV_LOG_PRINTF":1, - "LV_USE_FONT_SUBPX": 1, - "LV_FONT_SUBPX_BGR":1, - - "LV_USE_ASSERT_NULL":0, - "LV_USE_ASSERT_MALLOC":0, - "LV_USE_ASSERT_MEM_INTEGRITY":0, - "LV_USE_ASSERT_OBJ":0, - "LV_USE_ASSERT_STYLE":0, - - "LV_USE_USER_DATA": 1, - "LV_USE_LARGE_COORD": 1, - - "LV_FONT_MONTSERRAT_14":1, - "LV_FONT_MONTSERRAT_16":1, - "LV_FONT_MONTSERRAT_18":1, - "LV_FONT_MONTSERRAT_24":1, - "LV_FONT_MONTSERRAT_48":1, - "LV_FONT_MONTSERRAT_12_SUBPX":1, - "LV_FONT_MONTSERRAT_28_COMPRESSED":1, - "LV_FONT_DEJAVU_16_PERSIAN_HEBREW":1, - "LV_FONT_SIMSUN_16_CJK":1, - "LV_FONT_UNSCII_8":1, - "LV_FONT_UNSCII_16":1, - "LV_FONT_FMT_TXT_LARGE":1, - "LV_USE_FONT_COMPRESSED":1, - - "LV_USE_BIDI": 1, - "LV_USE_ARABIC_PERSIAN_CHARS":1, - - "LV_LABEL_TEXT_SELECTION":1, - - "LV_BUILD_EXAMPLES":1, - - "LV_FONT_DEFAULT":"&lv_font_montserrat_14", -} diff --git a/tests/main.py b/tests/main.py index 79898c81a..48346c0d4 100755 --- a/tests/main.py +++ b/tests/main.py @@ -1,55 +1,180 @@ #!/usr/bin/env python3 -import defines -import build +import glob import shutil -import test +import subprocess import sys import os +lvgl_test_dir = os.path.dirname(os.path.realpath(__file__)) +num_test_case_failures = 0 -def build_conf(title, defs): - print("") - print("") - print("============================================") - print(title) - print("============================================") - print("", flush=True) +# Key values must match variable names in CMakeLists.txt. +options = { + 'OPTIONS_MINIMAL_MONOCHROME': 'Minimal config monochrome', + 'OPTIONS_NORMAL_8BIT': 'Normal config, 8 bit color depth', + 'OPTIONS_16BIT': 'Minimal config, 16 bit color depth', + 'OPTIONS_16BIT_SWAP': 'Normal config, 16 bit color depth swapped', + 'OPTIONS_FULL_32BIT': 'Full config, 32 bit color depth', + 'OPTIONS_TEST': 'Test config, 32 bit color depth', +} - build.clean() - build.build(defs) +# Most test configurations only build - this is the one that also +# executes. +run_tests_option_name = 'OPTIONS_TEST' -test_only = "test" in sys.argv -test_report = "report" in sys.argv -test_noclean = "noclean" in sys.argv - -if not test_only: - build_conf("Minimal config monochrome", defines.minimal_monochrome) - build_conf("Normal config, 8 bit color depth", defines.normal_8bit) - build_conf("Minimal config, 16 bit color depth", defines.minimal_16bit) - build_conf("Normal config, 16 bit color depth swapped", - defines.normal_16bit_swap) - build_conf("Full config, 32 bit color depth", defines.full_32bit) - - -files = test.prepare() -if test_noclean == False: - build.clean() - -for f in files: - name = f[:-2] # test_foo.c -> test_foo - build.build_test(defines.test, name) - -if test_report: - print("") - print("Generating report") - print("-----------------------", flush=True) +def delete_dir_ignore_missing(dir_path): + '''Recursively delete a directory and ignore if missing.''' try: - shutil.rmtree('report') + shutil.rmtree(dir_path) except FileNotFoundError: pass + + +def generate_test_runners(): + '''Generate the test runner source code. + + Returns a list of test names.''' + global lvgl_test_dir + os.chdir(lvgl_test_dir) + delete_dir_ignore_missing('src/test_runners') + os.mkdir('src/test_runners') + + # TODO: Intermediate files should be in the build folders, not alongside + # the other repo source. + test_names = [] + for f in glob.glob("./src/test_cases/test_*.c"): + test_names.append(os.path.splitext(os.path.basename(f))[0]) + r = f[:-2] + "_Runner.c" + r = r.replace("/test_cases/", "/test_runners/") + subprocess.check_call(['ruby', 'unity/generate_test_runner.rb', + f, r, 'config.yml']) + return test_names + + +def options_abbrev(options_name): + '''Return an abbreviated version of the option name.''' + prefix = 'OPTIONS_' + assert options_name.startswith(prefix) + return options_name[len(prefix):].lower() + + +def get_build_dir(options_name): + '''Given the build options name, return the build directory name. + + Does not return the full path to the directory - just the base name.''' + return 'build_%s' % options_abbrev(options_name) + + +def delete_build_dir(options_name): + '''Recursively delete the build directory for the given options name.''' + global lvgl_test_dir + build_dir = os.path.join(lvgl_test_dir, get_build_dir(options_name)) + delete_dir_ignore_missing(build_dir) + + +def build_tests(options_name, build_type): + '''Build all tests for the specified options name.''' + global options, lvgl_test_dir, test_noclean + + print() + print() + label = 'Building: %s: %s' % (options_abbrev( + options_name), options[options_name]) + print('=' * len(label)) + print(label) + print('=' * len(label)) + print(flush=True) + + if not test_noclean: + delete_build_dir(options_name) + + os.chdir(lvgl_test_dir) + build_dir = get_build_dir(options_name) + created_build_dir = False + if not os.path.isdir(build_dir): + os.mkdir(build_dir) + created_build_dir = True + os.chdir(build_dir) + if created_build_dir: + subprocess.check_call(['cmake', '-DCMAKE_BUILD_TYPE=%s' % build_type, + '-D%s=1' % options_name, '..']) + subprocess.check_call(['cmake', '--build', os.path.join(lvgl_test_dir, build_dir), + '--parallel', str(os.cpu_count())]) + + +def run_tests(options_name, test_names): + '''Run the tests for the given options name.''' + global num_test_case_failures + relative_bd = get_build_dir(options_name) + abs_bd = os.path.join(lvgl_test_dir, relative_bd) + for test_name in test_names: + + print() + print() + label = 'Running: %s' % os.path.join( + options_abbrev(options_name), test_name) + print('=' * len(label)) + print(label) + print('=' * len(label), flush=True) + + try: + os.chdir(lvgl_test_dir) + subprocess.check_call([os.path.join(abs_bd, test_name)]) + except subprocess.CalledProcessError as e: + num_test_case_failures += 1 + + +def generate_code_coverage_report(): + '''Produce code coverage test reports for the test execution.''' + global lvgl_test_dir + + print() + print() + label = 'Generating code coverage reports' + print('=' * len(label)) + print(label) + print('=' * len(label)) + print(flush=True) + + os.chdir(lvgl_test_dir) + delete_dir_ignore_missing('report') os.mkdir('report') - os.system("gcovr -r ../ --html-details -o report/index.html --exclude-directories '\.\./examples' --exclude-directories 'src/.*' --exclude-directories 'unity' --exclude 'lv_test_.*\.c'") - os.system("gcovr -r ../ -x report/coverage.xml --exclude-directories '\.\./examples' --exclude-directories 'src/.*' --exclude-directories 'unity' --exclude 'lv_test_.*\.c'") - print("Done: See report/index.html", flush=True) + root_dir = os.pardir + html_report_file = 'report/index.html' + cmd = ['gcovr', '--root', root_dir, '--html-details', '--output', + html_report_file, '--xml', 'report/coverage.xml', + '-j', str(os.cpu_count()), '--print-summary', + '--html-title', 'LVGL Test Coverage'] + for d in ('.*\\bexamples/.*', '\\bsrc/test_.*', '\\bsrc/lv_test.*', '\\bunity\\b'): + cmd.extend(['--exclude', d]) + + subprocess.check_call(cmd) + print("Done: See %s" % html_report_file, flush=True) + + +run_test_only = "test" in sys.argv +generate_gcov_report = "report" in sys.argv +test_noclean = "noclean" in sys.argv + +test_names = generate_test_runners() + +for options_name in options.keys(): + is_test = options_name == run_tests_option_name + build_type = 'Release' if is_test else 'Debug' + if is_test or not run_test_only: + build_tests(options_name, build_type) + if options_name == run_tests_option_name: + run_tests(options_name, test_names) + +if generate_gcov_report: + generate_code_coverage_report() + +print() +if num_test_case_failures: + print('There were %d test case failures.' % + num_test_case_failures, file=sys.stderr) +else: + print('All test cases passed.') +sys.exit(num_test_case_failures) diff --git a/tests/src/lv_test_main.c b/tests/src/lv_test_main.c deleted file mode 100644 index 52f467601..000000000 --- a/tests/src/lv_test_main.c +++ /dev/null @@ -1,17 +0,0 @@ -#if LV_BUILD_TEST && !defined(LV_BUILD_TEST_NO_MAIN) -#include "../lvgl.h" -#include -#include - -#include -#include "lv_test_init.h" - -int main(void) -{ - lv_test_init(); - - printf("Exit with success!\n"); - return 0; -} - -#endif diff --git a/tests/test.py b/tests/test.py deleted file mode 100644 index 52ca66674..000000000 --- a/tests/test.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -import glob - -def prepare(): - os.system("rm src/*.o") - os.system("rm -rdf src/test_runners") - os.system("mkdir src/test_runners") - files = glob.glob("./src/test_cases/test_*.c") - - for index, item in enumerate(files): - if item == "./src/test_cases/test_config.c": - files.pop(index) - break - - files.insert(0, "./src/test_cases/test_config.c") - - for f in files: - r = f[:-2] + "_Runner.c" - r = r.replace("/test_cases/", "/test_runners/") - cmd = "ruby unity/generate_test_runner.rb " + f + " " + r + " config.yml" - os.system(cmd) - return files