pickman: Add test coverage support
Add support for running pickman tests with code coverage checking, similar to binman. Use test_util.run_test_suites() for running tests and test_util.run_test_coverage() for coverage checking. Options added to the 'test' command: -P: Number of processes for parallel test execution -T: Run with coverage checking -v: Verbosity level (0-4) The coverage check allows failures for modules that require external services (agent.py, gitlab_api.py, control.py, database.py) since these cannot easily achieve 100% coverage in unit tests. Also update test_util.py to recognize 'pickman' as using the 'test' subcommand like binman and patman. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com>
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
# Allow 'from pickman import xxx' to work via symlink
|
||||
our_path = os.path.dirname(os.path.realpath(__file__))
|
||||
@@ -16,6 +17,8 @@ sys.path.insert(0, os.path.join(our_path, '..'))
|
||||
|
||||
# pylint: disable=wrong-import-position,import-error
|
||||
from pickman import control
|
||||
from pickman import ftest
|
||||
from u_boot_pylib import test_util
|
||||
|
||||
|
||||
def parse_args(argv):
|
||||
@@ -80,11 +83,65 @@ def parse_args(argv):
|
||||
poll_cmd.add_argument('-t', '--target', default='master',
|
||||
help='Target branch for MR (default: master)')
|
||||
|
||||
subparsers.add_parser('test', help='Run tests')
|
||||
test_cmd = subparsers.add_parser('test', help='Run tests')
|
||||
test_cmd.add_argument('-P', '--processes', type=int,
|
||||
help='Number of processes to run tests (default: all)')
|
||||
test_cmd.add_argument('-T', '--test-coverage', action='store_true',
|
||||
help='Run tests and check for 100%% coverage')
|
||||
test_cmd.add_argument('-v', '--verbosity', type=int, default=1,
|
||||
help='Verbosity level (0-4, default: 1)')
|
||||
test_cmd.add_argument('tests', nargs='*', help='Specific tests to run')
|
||||
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def get_test_classes():
|
||||
"""Get all test classes from the ftest module.
|
||||
|
||||
Returns:
|
||||
list: List of test class objects
|
||||
"""
|
||||
return [getattr(ftest, name) for name in dir(ftest)
|
||||
if name.startswith('Test') and
|
||||
isinstance(getattr(ftest, name), type) and
|
||||
issubclass(getattr(ftest, name), unittest.TestCase)]
|
||||
|
||||
|
||||
def run_tests(processes, verbosity, test_name):
|
||||
"""Run the pickman test suite.
|
||||
|
||||
Args:
|
||||
processes (int): Number of processes for concurrent tests
|
||||
verbosity (int): Verbosity level (0-4)
|
||||
test_name (str): Specific test to run, or None for all
|
||||
|
||||
Returns:
|
||||
int: 0 if tests passed, 1 otherwise
|
||||
"""
|
||||
result = test_util.run_test_suites(
|
||||
'pickman', False, verbosity, False, False, processes,
|
||||
test_name, None, get_test_classes())
|
||||
|
||||
return 0 if result.wasSuccessful() else 1
|
||||
|
||||
|
||||
def run_test_coverage(args):
|
||||
"""Run tests with coverage checking.
|
||||
|
||||
Args:
|
||||
args (list): Specific tests to run, or None for all
|
||||
"""
|
||||
# agent.py and gitlab_api.py require external services (Claude, GitLab)
|
||||
# so they can't achieve 100% coverage in unit tests
|
||||
test_util.run_test_coverage(
|
||||
'tools/pickman/pickman', None,
|
||||
['*test*', '*__main__.py', 'tools/u_boot_pylib/*'],
|
||||
None, extra_args=None, args=args,
|
||||
allow_failures=['tools/pickman/agent.py',
|
||||
'tools/pickman/gitlab_api.py',
|
||||
'tools/pickman/control.py'])
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
"""Main function to parse args and run commands.
|
||||
|
||||
@@ -92,6 +149,14 @@ def main(argv=None):
|
||||
argv (list): Command line arguments (None for sys.argv[1:])
|
||||
"""
|
||||
args = parse_args(argv)
|
||||
|
||||
if args.cmd == 'test':
|
||||
if args.test_coverage:
|
||||
run_test_coverage(args.tests or None)
|
||||
return 0
|
||||
test_name = args.tests[0] if args.tests else None
|
||||
return run_tests(args.processes, args.verbosity, test_name)
|
||||
|
||||
return control.do_pickman(args)
|
||||
|
||||
|
||||
|
||||
@@ -57,7 +57,8 @@ def run_test_coverage(prog, filter_fname, exclude_list, build_dir,
|
||||
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'
|
||||
use_test = 'binman' in prog or 'patman' in prog or 'pickman' in prog
|
||||
test_cmd = 'test' if use_test else '-t'
|
||||
prefix = ''
|
||||
if build_dir:
|
||||
prefix = 'PYTHONPATH=$PYTHONPATH:%s/sandbox_spl/tools ' % build_dir
|
||||
@@ -91,13 +92,12 @@ def run_test_coverage(prog, filter_fname, exclude_list, build_dir,
|
||||
print(coverage)
|
||||
if coverage != '100%':
|
||||
print(stdout)
|
||||
print("To get a report in 'htmlcov/index.html', type: python3-coverage html")
|
||||
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:
|
||||
if allow_failures:
|
||||
# for line in lines:
|
||||
# print('.', line, re.match(r'^(tools/.*py) *\d+ *(\d+) *(\d+)%$', line))
|
||||
lines = [re.match(r'^(tools/.*py) *\d+ *(\d+) *\d+%$', line)
|
||||
for line in stdout.splitlines()]
|
||||
bad = []
|
||||
|
||||
Reference in New Issue
Block a user