diff options
Diffstat (limited to 'tools/patman/test_util.py')
-rw-r--r-- | tools/patman/test_util.py | 216 |
1 files changed, 0 insertions, 216 deletions
diff --git a/tools/patman/test_util.py b/tools/patman/test_util.py deleted file mode 100644 index 9e0811b61a..0000000000 --- a/tools/patman/test_util.py +++ /dev/null @@ -1,216 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0+ -# -# Copyright (c) 2016 Google, Inc -# - -from contextlib import contextmanager -import doctest -import glob -import multiprocessing -import os -import sys -import unittest - -from patman import command - -from io import StringIO - -use_concurrent = True -try: - from concurrencytest import ConcurrentTestSuite - from concurrencytest import fork_for_tests -except: - use_concurrent = False - - -def run_test_coverage(prog, filter_fname, exclude_list, build_dir, required=None, - extra_args=None): - """Run tests and check that we get 100% coverage - - Args: - prog: Program to run (with be passed a '-t' argument to run tests - filter_fname: Normally all *.py files in the program's directory will - be included. If this is not None, then it is used to filter the - list so that only filenames that don't contain filter_fname are - included. - exclude_list: List of file patterns to exclude from the coverage - calculation - build_dir: Build directory, used to locate libfdt.py - required: List of modules which must be in the coverage report - extra_args (str): Extra arguments to pass to the tool before the -t/test - arg - - Raises: - ValueError if the code coverage is not 100% - """ - # This uses the build output from sandbox_spl to get _libfdt.so - path = os.path.dirname(prog) - if filter_fname: - glob_list = glob.glob(os.path.join(path, '*.py')) - glob_list = [fname for fname in glob_list if filter_fname in fname] - else: - glob_list = [] - glob_list += exclude_list - glob_list += ['*libfdt.py', '*site-packages*', '*dist-packages*'] - glob_list += ['*concurrencytest*'] - test_cmd = 'test' if 'binman' in prog or 'patman' in prog else '-t' - prefix = '' - if build_dir: - prefix = 'PYTHONPATH=$PYTHONPATH:%s/sandbox_spl/tools ' % build_dir - cmd = ('%spython3-coverage run ' - '--omit "%s" %s %s %s -P1' % (prefix, ','.join(glob_list), - prog, extra_args or '', test_cmd)) - os.system(cmd) - stdout = command.output('python3-coverage', 'report') - lines = stdout.splitlines() - if required: - # Convert '/path/to/name.py' just the module name 'name' - test_set = set([os.path.splitext(os.path.basename(line.split()[0]))[0] - for line in lines if '/etype/' in line]) - missing_list = required - missing_list.discard('__init__') - missing_list.difference_update(test_set) - if missing_list: - print('Missing tests for %s' % (', '.join(missing_list))) - print(stdout) - ok = False - - coverage = lines[-1].split(' ')[-1] - ok = True - print(coverage) - if coverage != '100%': - print(stdout) - print("To get a report in 'htmlcov/index.html', type: python3-coverage html") - print('Coverage error: %s, but should be 100%%' % coverage) - ok = False - if not ok: - raise ValueError('Test coverage failure') - - -# Use this to suppress stdout/stderr output: -# with capture_sys_output() as (stdout, stderr) -# ...do something... -@contextmanager -def capture_sys_output(): - capture_out, capture_err = StringIO(), StringIO() - old_out, old_err = sys.stdout, sys.stderr - try: - sys.stdout, sys.stderr = capture_out, capture_err - yield capture_out, capture_err - finally: - sys.stdout, sys.stderr = old_out, old_err - - -class FullTextTestResult(unittest.TextTestResult): - """A test result class that can print extended text results to a stream - - This is meant to be used by a TestRunner as a result class. Like - TextTestResult, this prints out the names of tests as they are run, - errors as they occur, and a summary of the results at the end of the - test run. Beyond those, this prints information about skipped tests, - expected failures and unexpected successes. - - Args: - stream: A file-like object to write results to - descriptions (bool): True to print descriptions with test names - verbosity (int): Detail of printed output per test as they run - Test stdout and stderr always get printed when buffering - them is disabled by the test runner. In addition to that, - 0: Print nothing - 1: Print a dot per test - 2: Print test names - """ - def __init__(self, stream, descriptions, verbosity): - self.verbosity = verbosity - super().__init__(stream, descriptions, verbosity) - - def printErrors(self): - "Called by TestRunner after test run to summarize the tests" - # The parent class doesn't keep unexpected successes in the same - # format as the rest. Adapt it to what printErrorList expects. - unexpected_successes = [ - (test, 'Test was expected to fail, but succeeded.\n') - for test in self.unexpectedSuccesses - ] - - super().printErrors() # FAIL and ERROR - self.printErrorList('SKIP', self.skipped) - self.printErrorList('XFAIL', self.expectedFailures) - self.printErrorList('XPASS', unexpected_successes) - - def addSkip(self, test, reason): - """Called when a test is skipped.""" - # Add empty line to keep spacing consistent with other results - if not reason.endswith('\n'): - reason += '\n' - super().addSkip(test, reason) - - -def run_test_suites(toolname, debug, verbosity, test_preserve_dirs, processes, - test_name, toolpath, class_and_module_list): - """Run a series of test suites and collect the results - - Args: - toolname: Name of the tool that ran the tests - debug: True to enable debugging, which shows a full stack trace on error - verbosity: Verbosity level to use (0-4) - test_preserve_dirs: True to preserve the input directory used by tests - so that it can be examined afterwards (only useful for debugging - tests). If a single test is selected (in args[0]) it also preserves - the output directory for this test. Both directories are displayed - on the command line. - processes: Number of processes to use to run tests (None=same as #CPUs) - test_name: Name of test to run, or None for all - toolpath: List of paths to use for tools - class_and_module_list: List of test classes (type class) and module - names (type str) to run - """ - sys.argv = [sys.argv[0]] - if debug: - sys.argv.append('-D') - if verbosity: - sys.argv.append('-v%d' % verbosity) - if toolpath: - for path in toolpath: - sys.argv += ['--toolpath', path] - - suite = unittest.TestSuite() - loader = unittest.TestLoader() - runner = unittest.TextTestRunner( - stream=sys.stdout, - verbosity=(1 if verbosity is None else verbosity), - resultclass=FullTextTestResult, - ) - - if use_concurrent and processes != 1: - suite = ConcurrentTestSuite(suite, - fork_for_tests(processes or multiprocessing.cpu_count())) - - for module in class_and_module_list: - if isinstance(module, str) and (not test_name or test_name == module): - suite.addTests(doctest.DocTestSuite(module)) - - for module in class_and_module_list: - if isinstance(module, str): - continue - # Test the test module about our arguments, if it is interested - if hasattr(module, 'setup_test_args'): - setup_test_args = getattr(module, 'setup_test_args') - setup_test_args(preserve_indir=test_preserve_dirs, - preserve_outdirs=test_preserve_dirs and test_name is not None, - toolpath=toolpath, verbosity=verbosity) - if test_name: - # Since Python v3.5 If an ImportError or AttributeError occurs - # while traversing a name then a synthetic test that raises that - # error when run will be returned. Check that the requested test - # exists, otherwise these errors are included in the results. - if test_name in loader.getTestCaseNames(module): - suite.addTests(loader.loadTestsFromName(test_name, module)) - else: - suite.addTests(loader.loadTestsFromTestCase(module)) - - print(f" Running {toolname} tests ".center(70, "=")) - result = runner.run(suite) - print() - - return result |