Files
lvgl/tests/main.py
Chris Mumford 9c846ee493 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>
2021-08-27 15:49:31 +02:00

181 lines
5.6 KiB
Python
Executable File

#!/usr/bin/env python3
import glob
import shutil
import subprocess
import sys
import os
lvgl_test_dir = os.path.dirname(os.path.realpath(__file__))
num_test_case_failures = 0
# 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',
}
# Most test configurations only build - this is the one that also
# executes.
run_tests_option_name = 'OPTIONS_TEST'
def delete_dir_ignore_missing(dir_path):
'''Recursively delete a directory and ignore if missing.'''
try:
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')
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)