selftests: net: py: add test case filtering and listing

When developing new test cases and reproducing failures in
existing ones we currently have to run the entire test which
can take minutes to finish.

Add command line options for test selection, modeled after
kselftest_harness.h:

  -l       list tests (filtered, if filters were specified)
  -t name  include test
  -T name  exclude test

Since we don't have as clean separation into fixture / variant /
test as kselftest_harness this is not really a 1 to 1 match.
We have to lean on glob patterns instead.

Like in kselftest_harness filters are evaluated in order, first
match wins. If only exclusions are specified everything else is
included and vice versa.

Glob patterns (*, ?, [) are supported in addition to exact
matching.

Reviewed-by: Willem de Bruijn <willemb@google.com>
Tested-by: Gal Pressman <gal@nvidia.com>
Reviewed-by: Breno Leitao <leitao@debian.org>
Link: https://patch.msgid.link/20260410013921.1710295-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2026-04-09 18:39:21 -07:00
parent 0aa72fc37e
commit e46ff213f7

View File

@@ -1,6 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
import fnmatch
import functools
import getopt
import inspect
import os
import signal
@@ -32,6 +34,34 @@ class KsftTerminate(KeyboardInterrupt):
pass
class _KsftArgs:
def __init__(self):
self.list_tests = False
self.filters = []
try:
opts, _ = getopt.getopt(sys.argv[1:], 'hlt:T:')
except getopt.GetoptError as e:
print(e, file=sys.stderr)
sys.exit(1)
for opt, val in opts:
if opt == '-h':
print(f"Usage: {sys.argv[0]} [-h|-l] [-t|-T name]\n"
f"\t-h print help\n"
f"\t-l list tests (filtered, if filters were specified)\n"
f"\t-t name include test\n"
f"\t-T name exclude test",
file=sys.stderr)
sys.exit(0)
elif opt == '-l':
self.list_tests = True
elif opt == '-t':
self.filters.append((True, val))
elif opt == '-T':
self.filters.append((False, val))
@functools.lru_cache()
def _ksft_supports_color():
if os.environ.get("NO_COLOR") is not None:
@@ -298,8 +328,26 @@ def _ksft_intr(signum, frame):
ksft_pr(f"Ignoring SIGTERM (cnt: {term_cnt}), already exiting...")
def _ksft_generate_test_cases(cases, globs, case_pfx, args):
"""Generate a flat list of (func, args, name) tuples"""
def _ksft_name_matches(name, pattern):
if '*' in pattern or '?' in pattern or '[' in pattern:
return fnmatch.fnmatchcase(name, pattern)
return name == pattern
def _ksft_test_enabled(name, filters):
has_positive = False
for include, pattern in filters:
has_positive |= include
if _ksft_name_matches(name, pattern):
return include
return not has_positive
def _ksft_generate_test_cases(cases, globs, case_pfx, args, cli_args):
"""Generate a filtered list of (func, args, name) tuples.
If -l is given, prints matching test names and exits.
"""
cases = cases or []
test_cases = []
@@ -329,11 +377,22 @@ def _ksft_generate_test_cases(cases, globs, case_pfx, args):
else:
test_cases.append((func, args, func.__name__))
if cli_args.filters:
test_cases = [tc for tc in test_cases
if _ksft_test_enabled(tc[2], cli_args.filters)]
if cli_args.list_tests:
for _, _, name in test_cases:
print(name)
sys.exit(0)
return test_cases
def ksft_run(cases=None, globs=None, case_pfx=None, args=()):
test_cases = _ksft_generate_test_cases(cases, globs, case_pfx, args)
cli_args = _KsftArgs()
test_cases = _ksft_generate_test_cases(cases, globs, case_pfx, args,
cli_args)
global term_cnt
term_cnt = 0