forked from xuos/xiuos
				
			
		
			
				
	
	
		
			208 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			208 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
| #!/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': 'Test config, 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', '--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()
 |