diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-06-01 15:41:47 +1000 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-06-01 15:41:47 +1000 |
| commit | dd27c3530432ea0b09f01e604bf577f31d8ef841 (patch) | |
| tree | bbf86cf81a78f0ff0b07f31f1c390db473f26fd3 /lib/lvgl/tests/main.py | |
| parent | 6fd588e970470b15936187980829916d0dbe77bb (diff) | |
| download | tangara-fw-dd27c3530432ea0b09f01e604bf577f31d8ef841.tar.gz | |
convert lvgl from submodule to a plain old directory
Diffstat (limited to 'lib/lvgl/tests/main.py')
| m--------- | lib/lvgl | 0 | ||||
| -rwxr-xr-x | lib/lvgl/tests/main.py | 208 |
2 files changed, 208 insertions, 0 deletions
diff --git a/lib/lvgl b/lib/lvgl deleted file mode 160000 -Subproject 0732400e7b564dd0e7dc4a924619d8e19c5b23a diff --git a/lib/lvgl/tests/main.py b/lib/lvgl/tests/main.py new file mode 100755 index 00000000..efdfbe6c --- /dev/null +++ b/lib/lvgl/tests/main.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python3 + +import argparse +import errno +import glob +import shutil +import subprocess +import sys +import os + +lvgl_test_dir = os.path.dirname(os.path.realpath(__file__)) + +# Key values must match variable names in CMakeLists.txt. +build_only_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', +} + +test_options = { + 'OPTIONS_TEST_SYSHEAP': 'Test config, system heap, 32 bit color depth', + 'OPTIONS_TEST_DEFHEAP': 'Test config, LVGL heap, 32 bit color depth', +} + + +def is_valid_option_name(option_name): + return option_name in build_only_options or option_name in test_options + + +def get_option_description(option_name): + if option_name in build_only_options: + return build_only_options[option_name] + return test_options[option_name] + + +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.''' + 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. + for f in glob.glob("./src/test_cases/test_*.c"): + 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']) + + +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_base_buid_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 get_build_dir(options_name): + '''Given the build options name, return the build directory name. + + Returns absolute path to the build directory.''' + global lvgl_test_dir + return os.path.join(lvgl_test_dir, get_base_buid_dir(options_name)) + + +def build_tests(options_name, build_type, clean): + '''Build all tests for the specified options name.''' + global lvgl_test_dir + + print() + print() + label = 'Building: %s: %s' % (options_abbrev( + options_name), get_option_description(options_name)) + print('=' * len(label)) + print(label) + print('=' * len(label)) + print(flush=True) + + build_dir = get_build_dir(options_name) + if clean: + delete_dir_ignore_missing(build_dir) + + os.chdir(lvgl_test_dir) + 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', build_dir, + '--parallel', str(os.cpu_count())]) + + +def run_tests(options_name): + '''Run the tests for the given options name.''' + + print() + print() + label = 'Running tests for %s' % options_abbrev(options_name) + print('=' * len(label)) + print(label) + print('=' * len(label), flush=True) + + os.chdir(get_build_dir(options_name)) + subprocess.check_call( + ['ctest', '--timeout', '30', '--parallel', str(os.cpu_count()), '--output-on-failure']) + + +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) + + +if __name__ == "__main__": + epilog = '''This program builds and optionally runs the LVGL test programs. + There are two types of LVGL tests: "build", and "test". The build-only + tests, as their name suggests, only verify that the program successfully + compiles and links (with various build options). There are also a set of + tests that execute to verify correct LVGL library behavior. + ''' + parser = argparse.ArgumentParser( + description='Build and/or run LVGL tests.', epilog=epilog) + parser.add_argument('--build-options', nargs=1, + help='''the build option name to build or run. When + omitted all build configurations are used. + ''') + parser.add_argument('--clean', action='store_true', default=False, + help='clean existing build artifacts before operation.') + parser.add_argument('--report', action='store_true', + help='generate code coverage report for tests.') + parser.add_argument('actions', nargs='*', choices=['build', 'test'], + help='build: compile build tests, test: compile/run executable tests.') + + args = parser.parse_args() + + if args.build_options: + options_to_build = args.build_options + else: + if 'build' in args.actions: + if 'test' in args.actions: + options_to_build = {**build_only_options, **test_options} + else: + options_to_build = build_only_options + else: + options_to_build = test_options + + for opt in options_to_build: + if not is_valid_option_name(opt): + print('Invalid build option "%s"' % opt, file=sys.stderr) + sys.exit(errno.EINVAL) + + generate_test_runners() + + for options_name in options_to_build: + is_test = options_name in test_options + build_type = 'Debug' + build_tests(options_name, build_type, args.clean) + if is_test: + try: + run_tests(options_name) + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) + + if args.report: + generate_code_coverage_report() |
