test convert Makefile to CMake (#2495)

* Convert tests/Makefile to a cmake project file.

This change switches the building of LVGL tests to use cmake
which is more portable than make. Additionally, whenever
cmake can be used, instead of the Python script (`main.py`),
the former is preferred.

The interface to `main.py` is unchanged, and tests are built
and executated the same as before.

This closes https://github.com/lvgl/lvgl/issues/2474.

* Installing `gcovr` in GitHub workflow.

* Documented steps to install libpng-dev.

* Added missing stdout flush when running tests.

* Grammar tweak in README.

Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
Chris Mumford 2021-08-27 06:49:31 -07:00 committed by GitHub
parent 5dbea7d725
commit 9c846ee493
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 474 additions and 480 deletions

View File

@ -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'

3
.gitignore vendored
View File

@ -19,3 +19,6 @@ out_html
__pycache__
/emscripten_builder
test_screenshot_error.h
build/
tests/build_*/
tests/report/

View File

@ -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()

1
tests/.gitignore vendored
View File

@ -1,3 +1,2 @@
*.out
*_Runner.c
/report

294
tests/CMakeLists.txt Normal file
View File

@ -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
$<BUILD_INTERFACE:${LVGL_TEST_DIR}/src>
$<BUILD_INTERFACE:${LVGL_TEST_DIR}/unity>
$<BUILD_INTERFACE:${LVGL_TEST_DIR}>
)
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 $<BUILD_INTERFACE:${LVGL_PARENT_DIR}>)
# 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()

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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",
}

View File

@ -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)

View File

@ -1,17 +0,0 @@
#if LV_BUILD_TEST && !defined(LV_BUILD_TEST_NO_MAIN)
#include "../lvgl.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include "lv_test_init.h"
int main(void)
{
lv_test_init();
printf("Exit with success!\n");
return 0;
}
#endif

View File

@ -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