test(ci) build and run tests in parallel. (#2515)
Create one GitHub workflow job for each build option build
or test execution. This allows each of the five builds to
be executed in parallel as well as making it easier to
navigate to the test output as it now resides in its own
job.
This change **does** change the command-line arguments to
the test execution script. This is required to allow the
build options name to be passed in.
New `tests/main.py` command-line help:
```
usage: main.py [-h] [--build-options BUILD_OPTIONS] [--clean]
[--report]
[{build,test} [{build,test} ...]]
Build and/or run LVGL tests.
positional arguments:
{build,test} build: compile build tests, test:
compile/run executable tests.
optional arguments:
-h, --help show this help message and exit
--build-options BUILD_OPTIONS
the build option name to build or run.
When omitted all build configurations
are used.
--clean clean existing build artifacts before
operation.
--report generate code coverage report for
tests.
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.
```
This commit is contained in:
@@ -191,17 +191,17 @@ set(LVGL_TEST_OPTIONS_FULL_32BIT
|
||||
)
|
||||
|
||||
if (OPTIONS_MINIMAL_MONOCHROME)
|
||||
set (TEST_OPTIONS ${LVGL_TEST_OPTIONS_MINIMAL_MONOCHROME})
|
||||
set (BUILD_OPTIONS ${LVGL_TEST_OPTIONS_MINIMAL_MONOCHROME})
|
||||
elseif (OPTIONS_NORMAL_8BIT)
|
||||
set (TEST_OPTIONS ${LVGL_TEST_OPTIONS_NORMAL_8BIT})
|
||||
set (BUILD_OPTIONS ${LVGL_TEST_OPTIONS_NORMAL_8BIT})
|
||||
elseif (OPTIONS_16BIT)
|
||||
set (TEST_OPTIONS ${LVGL_TEST_OPTIONS_16BIT})
|
||||
set (BUILD_OPTIONS ${LVGL_TEST_OPTIONS_16BIT})
|
||||
elseif (OPTIONS_16BIT_SWAP)
|
||||
set (TEST_OPTIONS ${LVGL_TEST_OPTIONS_16BIT_SWAP})
|
||||
set (BUILD_OPTIONS ${LVGL_TEST_OPTIONS_16BIT_SWAP})
|
||||
elseif (OPTIONS_FULL_32BIT)
|
||||
set (TEST_OPTIONS ${LVGL_TEST_OPTIONS_FULL_32BIT})
|
||||
set (BUILD_OPTIONS ${LVGL_TEST_OPTIONS_FULL_32BIT})
|
||||
elseif (OPTIONS_TEST)
|
||||
set (TEST_OPTIONS ${LVGL_TEST_OPTIONS_TEST})
|
||||
set (BUILD_OPTIONS ${LVGL_TEST_OPTIONS_TEST})
|
||||
set (TEST_LIBS --coverage)
|
||||
else()
|
||||
message(FATAL_ERROR "Must provide an options value.")
|
||||
@@ -235,7 +235,7 @@ set(COMPILE_OPTIONS
|
||||
-Wundef
|
||||
-Wuninitialized
|
||||
-Wunreachable-code
|
||||
${TEST_OPTIONS}
|
||||
${BUILD_OPTIONS}
|
||||
)
|
||||
|
||||
get_filename_component(LVGL_DIR ${LVGL_TEST_DIR} DIRECTORY)
|
||||
|
||||
@@ -13,16 +13,13 @@ scripts/install-prerequisites.sh
|
||||
```
|
||||
|
||||
### Run test
|
||||
1. Enter `lvgl/tests/`
|
||||
2. Run the tests with `./main.py [OPTIONS]`. The options are
|
||||
- `report` Create a html page in the `report` folder with the coverage report.
|
||||
- `test` Build and run only test. Without this option LVGL will be built with various configurations.
|
||||
- `noclean` Do not clean the project before building. Useful while writing test to save some times.
|
||||
|
||||
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 used when writing tests.
|
||||
1. Run all executable tests with `./tests/main.py test`.
|
||||
2. Build all build-only tests with `./tests/main.py build`.
|
||||
3. Clean prior test build, build all build-only tests,
|
||||
run executable tests, and generate code coverage
|
||||
report `./tests/main.py --clean --report build test`.
|
||||
|
||||
For full information on running tests run: `./tests/main.py --help`.
|
||||
|
||||
## Running automatically
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import errno
|
||||
import glob
|
||||
import shutil
|
||||
import subprocess
|
||||
@@ -9,18 +11,27 @@ import os
|
||||
lvgl_test_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
# Key values must match variable names in CMakeLists.txt.
|
||||
options = {
|
||||
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': '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 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):
|
||||
@@ -69,29 +80,24 @@ def get_build_dir(options_name):
|
||||
return os.path.join(lvgl_test_dir, get_base_buid_dir(options_name))
|
||||
|
||||
|
||||
def delete_build_dir(options_name):
|
||||
'''Recursively delete the build directory for the given options name.'''
|
||||
delete_dir_ignore_missing(get_build_dir(options_name))
|
||||
|
||||
|
||||
def build_tests(options_name, build_type):
|
||||
def build_tests(options_name, build_type, clean):
|
||||
'''Build all tests for the specified options name.'''
|
||||
global options, lvgl_test_dir, test_noclean
|
||||
global lvgl_test_dir
|
||||
|
||||
print()
|
||||
print()
|
||||
label = 'Building: %s: %s' % (options_abbrev(
|
||||
options_name), options[options_name])
|
||||
options_name), get_option_description(options_name))
|
||||
print('=' * len(label))
|
||||
print(label)
|
||||
print('=' * len(label))
|
||||
print(flush=True)
|
||||
|
||||
if not test_noclean:
|
||||
delete_build_dir(options_name)
|
||||
build_dir = get_build_dir(options_name)
|
||||
if clean:
|
||||
delete_dir_ignore_missing(build_dir)
|
||||
|
||||
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)
|
||||
@@ -147,24 +153,55 @@ def generate_code_coverage_report():
|
||||
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
|
||||
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.')
|
||||
|
||||
generate_test_runners()
|
||||
args = parser.parse_args()
|
||||
|
||||
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:
|
||||
try:
|
||||
run_tests(options_name)
|
||||
except subprocess.CalledProcessError as e:
|
||||
sys.exit(e.returncode)
|
||||
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)
|
||||
|
||||
if generate_gcov_report:
|
||||
generate_code_coverage_report()
|
||||
generate_test_runners()
|
||||
|
||||
for options_name in options_to_build:
|
||||
is_test = options_name in test_options
|
||||
build_type = 'Release' if is_test else '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()
|
||||
|
||||
Reference in New Issue
Block a user