diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index f40875204c..5fed0e8e1c 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -8,20 +8,38 @@ on: jobs: build: - runs-on: ubuntu-latest + strategy: + matrix: + # A valid option parameter to the cmake file. + # See BUILD_OPTIONS in tests/CMakeLists.txt. + build_option: ['OPTIONS_MINIMAL_MONOCHROME', + 'OPTIONS_NORMAL_8BIT', + 'OPTIONS_16BIT', + 'OPTIONS_16BIT_SWAP', + 'OPTIONS_FULL_32BIT'] + name: Build ${{ matrix.build_option }} + steps: + - uses: actions/checkout@v2 + - uses: ammaraskar/gcc-problem-matcher@master + - name: Install prerequisites + run: scripts/install-prerequisites.sh + - name: Building ${{ matrix.build_option }} + run: python tests/main.py --build-option=${{ matrix.build_option }} build + test: + runs-on: ubuntu-latest + name: Executable Tests steps: - uses: actions/checkout@v2 - uses: ammaraskar/gcc-problem-matcher@master - name: Install prerequisites run: scripts/install-prerequisites.sh - name: Run tests - run: python tests/main.py report + run: python tests/main.py --report test - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 if: github.event_name == 'push' with: fail_ci_if_error: true - verbose: true - + verbose: true \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2754c61bea..c7cd3e8a7e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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) diff --git a/tests/README.md b/tests/README.md index f2215be18e..12b41c7e32 100644 --- a/tests/README.md +++ b/tests/README.md @@ -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 diff --git a/tests/main.py b/tests/main.py index f1526264a8..b283805e49 100755 --- a/tests/main.py +++ b/tests/main.py @@ -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()