1ff1fd6ccSSimon Glass# SPDX-License-Identifier: GPL-2.0+ 2ff1fd6ccSSimon Glass# 3ff1fd6ccSSimon Glass# Copyright (c) 2016 Google, Inc 4ff1fd6ccSSimon Glass# 5ff1fd6ccSSimon Glass 6c3f94541SSimon Glassfrom contextlib import contextmanager 7ff1fd6ccSSimon Glassimport glob 8ff1fd6ccSSimon Glassimport os 9ff1fd6ccSSimon Glassimport sys 10ff1fd6ccSSimon Glass 11ff1fd6ccSSimon Glassimport command 12ff1fd6ccSSimon Glass 13c3f94541SSimon Glasstry: 14c3f94541SSimon Glass from StringIO import StringIO 15c3f94541SSimon Glassexcept ImportError: 16c3f94541SSimon Glass from io import StringIO 17c3f94541SSimon Glass 18c3f94541SSimon Glass 19ff1fd6ccSSimon Glassdef RunTestCoverage(prog, filter_fname, exclude_list, build_dir, required=None): 20ff1fd6ccSSimon Glass """Run tests and check that we get 100% coverage 21ff1fd6ccSSimon Glass 22ff1fd6ccSSimon Glass Args: 23ff1fd6ccSSimon Glass prog: Program to run (with be passed a '-t' argument to run tests 24ff1fd6ccSSimon Glass filter_fname: Normally all *.py files in the program's directory will 25ff1fd6ccSSimon Glass be included. If this is not None, then it is used to filter the 26ff1fd6ccSSimon Glass list so that only filenames that don't contain filter_fname are 27ff1fd6ccSSimon Glass included. 28ff1fd6ccSSimon Glass exclude_list: List of file patterns to exclude from the coverage 29ff1fd6ccSSimon Glass calculation 30ff1fd6ccSSimon Glass build_dir: Build directory, used to locate libfdt.py 31ff1fd6ccSSimon Glass required: List of modules which must be in the coverage report 32ff1fd6ccSSimon Glass 33ff1fd6ccSSimon Glass Raises: 34ff1fd6ccSSimon Glass ValueError if the code coverage is not 100% 35ff1fd6ccSSimon Glass """ 36ff1fd6ccSSimon Glass # This uses the build output from sandbox_spl to get _libfdt.so 37ff1fd6ccSSimon Glass path = os.path.dirname(prog) 38ff1fd6ccSSimon Glass if filter_fname: 39ff1fd6ccSSimon Glass glob_list = glob.glob(os.path.join(path, '*.py')) 40ff1fd6ccSSimon Glass glob_list = [fname for fname in glob_list if filter_fname in fname] 41ff1fd6ccSSimon Glass else: 42ff1fd6ccSSimon Glass glob_list = [] 43ff1fd6ccSSimon Glass glob_list += exclude_list 44ff1fd6ccSSimon Glass glob_list += ['*libfdt.py', '*site-packages*'] 45ff1fd6ccSSimon Glass cmd = ('PYTHONPATH=$PYTHONPATH:%s/sandbox_spl/tools python-coverage run ' 46*11ae93eeSSimon Glass '--omit "%s" %s -P1 -t' % (build_dir, ','.join(glob_list), prog)) 47ff1fd6ccSSimon Glass os.system(cmd) 48ff1fd6ccSSimon Glass stdout = command.Output('python-coverage', 'report') 49ff1fd6ccSSimon Glass lines = stdout.splitlines() 50ff1fd6ccSSimon Glass if required: 51ff1fd6ccSSimon Glass # Convert '/path/to/name.py' just the module name 'name' 52ff1fd6ccSSimon Glass test_set = set([os.path.splitext(os.path.basename(line.split()[0]))[0] 53ff1fd6ccSSimon Glass for line in lines if '/etype/' in line]) 54ff1fd6ccSSimon Glass missing_list = required 55ff1fd6ccSSimon Glass missing_list.difference_update(test_set) 56ff1fd6ccSSimon Glass if missing_list: 57ff1fd6ccSSimon Glass print 'Missing tests for %s' % (', '.join(missing_list)) 58ff1fd6ccSSimon Glass print stdout 59ff1fd6ccSSimon Glass ok = False 60ff1fd6ccSSimon Glass 61ff1fd6ccSSimon Glass coverage = lines[-1].split(' ')[-1] 62ff1fd6ccSSimon Glass ok = True 63ff1fd6ccSSimon Glass print coverage 64ff1fd6ccSSimon Glass if coverage != '100%': 65ff1fd6ccSSimon Glass print stdout 66ff1fd6ccSSimon Glass print ("Type 'python-coverage html' to get a report in " 67ff1fd6ccSSimon Glass 'htmlcov/index.html') 68ff1fd6ccSSimon Glass print 'Coverage error: %s, but should be 100%%' % coverage 69ff1fd6ccSSimon Glass ok = False 70ff1fd6ccSSimon Glass if not ok: 71ff1fd6ccSSimon Glass raise ValueError('Test coverage failure') 72c3f94541SSimon Glass 73c3f94541SSimon Glass 74c3f94541SSimon Glass# Use this to suppress stdout/stderr output: 75c3f94541SSimon Glass# with capture_sys_output() as (stdout, stderr) 76c3f94541SSimon Glass# ...do something... 77c3f94541SSimon Glass@contextmanager 78c3f94541SSimon Glassdef capture_sys_output(): 79c3f94541SSimon Glass capture_out, capture_err = StringIO(), StringIO() 80c3f94541SSimon Glass old_out, old_err = sys.stdout, sys.stderr 81c3f94541SSimon Glass try: 82c3f94541SSimon Glass sys.stdout, sys.stderr = capture_out, capture_err 83c3f94541SSimon Glass yield capture_out, capture_err 84c3f94541SSimon Glass finally: 85c3f94541SSimon Glass sys.stdout, sys.stderr = old_out, old_err 86