2024-04-04 19:45:22 -07:00
|
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
|
|
|
|
|
|
import builtins
|
2024-08-01 17:03:08 -07:00
|
|
|
import functools
|
2024-04-12 07:14:33 -07:00
|
|
|
import inspect
|
2025-05-02 18:18:56 -07:00
|
|
|
import signal
|
2024-04-17 16:11:40 -07:00
|
|
|
import sys
|
2024-04-12 07:14:36 -07:00
|
|
|
import time
|
2024-04-12 07:14:34 -07:00
|
|
|
import traceback
|
2024-04-04 19:45:22 -07:00
|
|
|
from .consts import KSFT_MAIN_NAME
|
selftests: drv-net: add ability to schedule cleanup with defer()
This implements what I was describing in [1]. When writing a test
author can schedule cleanup / undo actions right after the creation
completes, eg:
cmd("touch /tmp/file")
defer(cmd, "rm /tmp/file")
defer() takes the function name as first argument, and the rest are
arguments for that function. defer()red functions are called in
inverse order after test exits. It's also possible to capture them
and execute earlier (in which case they get automatically de-queued).
undo = defer(cmd, "rm /tmp/file")
# ... some unsafe code ...
undo.exec()
As a nice safety all exceptions from defer()ed calls are captured,
printed, and ignored (they do make the test fail, however).
This addresses the common problem of exceptions in cleanup paths
often being unhandled, leading to potential leaks.
There is a global action queue, flushed by ksft_run(). We could support
function level defers too, I guess, but there's no immediate need..
Link: https://lore.kernel.org/all/877cedb2ki.fsf@nvidia.com/ # [1]
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Reviewed-by: Petr Machata <petrm@nvidia.com>
Link: https://patch.msgid.link/20240627185502.3069139-3-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2024-06-27 11:55:01 -07:00
|
|
|
from .utils import global_defer_queue
|
2024-04-04 19:45:22 -07:00
|
|
|
|
|
|
|
KSFT_RESULT = None
|
2024-04-17 16:11:40 -07:00
|
|
|
KSFT_RESULT_ALL = True
|
2024-08-01 17:03:08 -07:00
|
|
|
KSFT_DISRUPTIVE = True
|
2024-04-04 19:45:22 -07:00
|
|
|
|
|
|
|
|
2024-04-29 07:44:26 -07:00
|
|
|
class KsftFailEx(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2024-04-04 19:45:22 -07:00
|
|
|
class KsftSkipEx(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class KsftXfailEx(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2025-05-02 18:18:56 -07:00
|
|
|
class KsftTerminate(KeyboardInterrupt):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2024-04-04 19:45:22 -07:00
|
|
|
def ksft_pr(*objs, **kwargs):
|
2025-07-16 13:57:12 -07:00
|
|
|
kwargs["flush"] = True
|
2024-04-04 19:45:22 -07:00
|
|
|
print("#", *objs, **kwargs)
|
|
|
|
|
|
|
|
|
2024-04-12 07:14:33 -07:00
|
|
|
def _fail(*args):
|
|
|
|
global KSFT_RESULT
|
|
|
|
KSFT_RESULT = False
|
|
|
|
|
selftests: net: ksft: print more of the stack for checks
Print more stack frames and the failing line when check fails.
This helps when tests use helpers to do the checks.
Before:
# At ./ksft/drivers/net/hw/rss_ctx.py line 92:
# Check failed 1037698 >= 396893.0 traffic on other queues:[344612, 462380, 233020, 449174, 342298]
not ok 8 rss_ctx.test_rss_context_queue_reconfigure
After:
# Check| At ./ksft/drivers/net/hw/rss_ctx.py, line 387, in test_rss_context_queue_reconfigure:
# Check| test_rss_queue_reconfigure(cfg, main_ctx=False)
# Check| At ./ksft/drivers/net/hw/rss_ctx.py, line 230, in test_rss_queue_reconfigure:
# Check| _send_traffic_check(cfg, port, ctx_ref, { 'target': (0, 3),
# Check| At ./ksft/drivers/net/hw/rss_ctx.py, line 92, in _send_traffic_check:
# Check| ksft_lt(sum(cnts[i] for i in params['noise']), directed / 2,
# Check failed 1045235 >= 405823.5 traffic on other queues (context 1)':[460068, 351995, 565970, 351579, 127270]
not ok 8 rss_ctx.test_rss_context_queue_reconfigure
Link: https://patch.msgid.link/20240801232317.545577-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2024-08-01 16:23:17 -07:00
|
|
|
stack = inspect.stack()
|
|
|
|
started = False
|
|
|
|
for frame in reversed(stack[2:]):
|
|
|
|
# Start printing from the test case function
|
|
|
|
if not started:
|
|
|
|
if frame.function == 'ksft_run':
|
|
|
|
started = True
|
|
|
|
continue
|
|
|
|
|
|
|
|
ksft_pr("Check| At " + frame.filename + ", line " + str(frame.lineno) +
|
|
|
|
", in " + frame.function + ":")
|
|
|
|
ksft_pr("Check| " + frame.code_context[0].strip())
|
2024-04-12 07:14:33 -07:00
|
|
|
ksft_pr(*args)
|
|
|
|
|
|
|
|
|
2024-04-04 19:45:22 -07:00
|
|
|
def ksft_eq(a, b, comment=""):
|
|
|
|
global KSFT_RESULT
|
|
|
|
if a != b:
|
2024-04-12 07:14:33 -07:00
|
|
|
_fail("Check failed", a, "!=", b, comment)
|
2024-04-04 19:45:22 -07:00
|
|
|
|
|
|
|
|
2024-08-09 22:37:28 -07:00
|
|
|
def ksft_ne(a, b, comment=""):
|
|
|
|
global KSFT_RESULT
|
|
|
|
if a == b:
|
|
|
|
_fail("Check failed", a, "==", b, comment)
|
|
|
|
|
|
|
|
|
2024-04-04 19:45:22 -07:00
|
|
|
def ksft_true(a, comment=""):
|
|
|
|
if not a:
|
2024-04-12 07:14:33 -07:00
|
|
|
_fail("Check failed", a, "does not eval to True", comment)
|
2024-04-04 19:45:22 -07:00
|
|
|
|
|
|
|
|
|
|
|
def ksft_in(a, b, comment=""):
|
|
|
|
if a not in b:
|
2024-04-12 07:14:33 -07:00
|
|
|
_fail("Check failed", a, "not in", b, comment)
|
2024-04-04 19:45:22 -07:00
|
|
|
|
|
|
|
|
2025-02-19 15:49:55 -08:00
|
|
|
def ksft_not_in(a, b, comment=""):
|
|
|
|
if a in b:
|
|
|
|
_fail("Check failed", a, "in", b, comment)
|
|
|
|
|
|
|
|
|
2025-01-06 18:29:32 -08:00
|
|
|
def ksft_is(a, b, comment=""):
|
|
|
|
if a is not b:
|
|
|
|
_fail("Check failed", a, "is not", b, comment)
|
|
|
|
|
|
|
|
|
2024-04-04 19:45:22 -07:00
|
|
|
def ksft_ge(a, b, comment=""):
|
|
|
|
if a < b:
|
2024-04-12 07:14:33 -07:00
|
|
|
_fail("Check failed", a, "<", b, comment)
|
2024-04-04 19:45:22 -07:00
|
|
|
|
|
|
|
|
2024-06-25 18:24:56 -07:00
|
|
|
def ksft_lt(a, b, comment=""):
|
|
|
|
if a >= b:
|
|
|
|
_fail("Check failed", a, ">=", b, comment)
|
|
|
|
|
|
|
|
|
2024-04-19 19:35:42 -07:00
|
|
|
class ksft_raises:
|
|
|
|
def __init__(self, expected_type):
|
|
|
|
self.exception = None
|
|
|
|
self.expected_type = expected_type
|
|
|
|
|
|
|
|
def __enter__(self):
|
|
|
|
return self
|
|
|
|
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
|
|
if exc_type is None:
|
|
|
|
_fail(f"Expected exception {str(self.expected_type.__name__)}, none raised")
|
|
|
|
elif self.expected_type != exc_type:
|
|
|
|
_fail(f"Expected exception {str(self.expected_type.__name__)}, raised {str(exc_type.__name__)}")
|
|
|
|
self.exception = exc_val
|
|
|
|
# Suppress the exception if its the expected one
|
|
|
|
return self.expected_type == exc_type
|
|
|
|
|
|
|
|
|
2024-04-12 07:14:36 -07:00
|
|
|
def ksft_busy_wait(cond, sleep=0.005, deadline=1, comment=""):
|
|
|
|
end = time.monotonic() + deadline
|
|
|
|
while True:
|
|
|
|
if cond():
|
|
|
|
return
|
|
|
|
if time.monotonic() > end:
|
|
|
|
_fail("Waiting for condition timed out", comment)
|
|
|
|
return
|
|
|
|
time.sleep(sleep)
|
|
|
|
|
|
|
|
|
2024-04-04 19:45:22 -07:00
|
|
|
def ktap_result(ok, cnt=1, case="", comment=""):
|
2024-04-17 16:11:40 -07:00
|
|
|
global KSFT_RESULT_ALL
|
|
|
|
KSFT_RESULT_ALL = KSFT_RESULT_ALL and ok
|
|
|
|
|
2024-04-04 19:45:22 -07:00
|
|
|
res = ""
|
|
|
|
if not ok:
|
|
|
|
res += "not "
|
|
|
|
res += "ok "
|
|
|
|
res += str(cnt) + " "
|
|
|
|
res += KSFT_MAIN_NAME
|
|
|
|
if case:
|
|
|
|
res += "." + str(case.__name__)
|
|
|
|
if comment:
|
|
|
|
res += " # " + comment
|
2025-07-16 13:57:12 -07:00
|
|
|
print(res, flush=True)
|
2024-04-04 19:45:22 -07:00
|
|
|
|
|
|
|
|
selftests: drv-net: add ability to schedule cleanup with defer()
This implements what I was describing in [1]. When writing a test
author can schedule cleanup / undo actions right after the creation
completes, eg:
cmd("touch /tmp/file")
defer(cmd, "rm /tmp/file")
defer() takes the function name as first argument, and the rest are
arguments for that function. defer()red functions are called in
inverse order after test exits. It's also possible to capture them
and execute earlier (in which case they get automatically de-queued).
undo = defer(cmd, "rm /tmp/file")
# ... some unsafe code ...
undo.exec()
As a nice safety all exceptions from defer()ed calls are captured,
printed, and ignored (they do make the test fail, however).
This addresses the common problem of exceptions in cleanup paths
often being unhandled, leading to potential leaks.
There is a global action queue, flushed by ksft_run(). We could support
function level defers too, I guess, but there's no immediate need..
Link: https://lore.kernel.org/all/877cedb2ki.fsf@nvidia.com/ # [1]
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Reviewed-by: Petr Machata <petrm@nvidia.com>
Link: https://patch.msgid.link/20240627185502.3069139-3-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2024-06-27 11:55:01 -07:00
|
|
|
def ksft_flush_defer():
|
|
|
|
global KSFT_RESULT
|
|
|
|
|
|
|
|
i = 0
|
|
|
|
qlen_start = len(global_defer_queue)
|
|
|
|
while global_defer_queue:
|
|
|
|
i += 1
|
|
|
|
entry = global_defer_queue.pop()
|
|
|
|
try:
|
|
|
|
entry.exec_only()
|
|
|
|
except:
|
|
|
|
ksft_pr(f"Exception while handling defer / cleanup (callback {i} of {qlen_start})!")
|
|
|
|
tb = traceback.format_exc()
|
|
|
|
for line in tb.strip().split('\n'):
|
|
|
|
ksft_pr("Defer Exception|", line)
|
|
|
|
KSFT_RESULT = False
|
|
|
|
|
|
|
|
|
2024-08-01 17:03:08 -07:00
|
|
|
def ksft_disruptive(func):
|
|
|
|
"""
|
|
|
|
Decorator that marks the test as disruptive (e.g. the test
|
|
|
|
that can down the interface). Disruptive tests can be skipped
|
|
|
|
by passing DISRUPTIVE=False environment variable.
|
|
|
|
"""
|
|
|
|
|
|
|
|
@functools.wraps(func)
|
|
|
|
def wrapper(*args, **kwargs):
|
|
|
|
if not KSFT_DISRUPTIVE:
|
|
|
|
raise KsftSkipEx(f"marked as disruptive")
|
|
|
|
return func(*args, **kwargs)
|
|
|
|
return wrapper
|
|
|
|
|
|
|
|
|
|
|
|
def ksft_setup(env):
|
|
|
|
"""
|
|
|
|
Setup test framework global state from the environment.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def get_bool(env, name):
|
|
|
|
value = env.get(name, "").lower()
|
|
|
|
if value in ["yes", "true"]:
|
|
|
|
return True
|
|
|
|
if value in ["no", "false"]:
|
|
|
|
return False
|
|
|
|
try:
|
|
|
|
return bool(int(value))
|
|
|
|
except:
|
|
|
|
raise Exception(f"failed to parse {name}")
|
|
|
|
|
|
|
|
if "DISRUPTIVE" in env:
|
|
|
|
global KSFT_DISRUPTIVE
|
|
|
|
KSFT_DISRUPTIVE = get_bool(env, "DISRUPTIVE")
|
|
|
|
|
|
|
|
return env
|
|
|
|
|
|
|
|
|
2025-05-02 18:18:56 -07:00
|
|
|
def _ksft_intr(signum, frame):
|
|
|
|
# ksft runner.sh sends 2 SIGTERMs in a row on a timeout
|
|
|
|
# if we don't ignore the second one it will stop us from handling cleanup
|
|
|
|
global term_cnt
|
|
|
|
term_cnt += 1
|
|
|
|
if term_cnt == 1:
|
|
|
|
raise KsftTerminate()
|
|
|
|
else:
|
|
|
|
ksft_pr(f"Ignoring SIGTERM (cnt: {term_cnt}), already exiting...")
|
|
|
|
|
|
|
|
|
2024-04-19 19:52:35 -07:00
|
|
|
def ksft_run(cases=None, globs=None, case_pfx=None, args=()):
|
|
|
|
cases = cases or []
|
|
|
|
|
|
|
|
if globs and case_pfx:
|
|
|
|
for key, value in globs.items():
|
|
|
|
if not callable(value):
|
|
|
|
continue
|
|
|
|
for prefix in case_pfx:
|
|
|
|
if key.startswith(prefix):
|
|
|
|
cases.append(value)
|
|
|
|
break
|
|
|
|
|
2025-05-02 18:18:56 -07:00
|
|
|
global term_cnt
|
|
|
|
term_cnt = 0
|
|
|
|
prev_sigterm = signal.signal(signal.SIGTERM, _ksft_intr)
|
|
|
|
|
2024-04-04 19:45:22 -07:00
|
|
|
totals = {"pass": 0, "fail": 0, "skip": 0, "xfail": 0}
|
|
|
|
|
2025-07-16 13:57:12 -07:00
|
|
|
print("TAP version 13", flush=True)
|
|
|
|
print("1.." + str(len(cases)), flush=True)
|
2024-04-04 19:45:22 -07:00
|
|
|
|
|
|
|
global KSFT_RESULT
|
|
|
|
cnt = 0
|
2024-07-04 18:52:22 -07:00
|
|
|
stop = False
|
2024-04-04 19:45:22 -07:00
|
|
|
for case in cases:
|
|
|
|
KSFT_RESULT = True
|
|
|
|
cnt += 1
|
2024-06-27 11:55:00 -07:00
|
|
|
comment = ""
|
|
|
|
cnt_key = ""
|
|
|
|
|
2024-04-04 19:45:22 -07:00
|
|
|
try:
|
|
|
|
case(*args)
|
|
|
|
except KsftSkipEx as e:
|
2024-06-27 11:55:00 -07:00
|
|
|
comment = "SKIP " + str(e)
|
|
|
|
cnt_key = 'skip'
|
2024-04-04 19:45:22 -07:00
|
|
|
except KsftXfailEx as e:
|
2024-06-27 11:55:00 -07:00
|
|
|
comment = "XFAIL " + str(e)
|
|
|
|
cnt_key = 'xfail'
|
2024-07-04 18:52:22 -07:00
|
|
|
except BaseException as e:
|
|
|
|
stop |= isinstance(e, KeyboardInterrupt)
|
2024-04-12 07:14:34 -07:00
|
|
|
tb = traceback.format_exc()
|
|
|
|
for line in tb.strip().split('\n'):
|
2024-04-04 19:45:22 -07:00
|
|
|
ksft_pr("Exception|", line)
|
2024-07-04 18:52:22 -07:00
|
|
|
if stop:
|
2025-05-02 18:18:56 -07:00
|
|
|
ksft_pr(f"Stopping tests due to {type(e).__name__}.")
|
2024-06-27 11:55:00 -07:00
|
|
|
KSFT_RESULT = False
|
|
|
|
cnt_key = 'fail'
|
|
|
|
|
selftests: drv-net: add ability to schedule cleanup with defer()
This implements what I was describing in [1]. When writing a test
author can schedule cleanup / undo actions right after the creation
completes, eg:
cmd("touch /tmp/file")
defer(cmd, "rm /tmp/file")
defer() takes the function name as first argument, and the rest are
arguments for that function. defer()red functions are called in
inverse order after test exits. It's also possible to capture them
and execute earlier (in which case they get automatically de-queued).
undo = defer(cmd, "rm /tmp/file")
# ... some unsafe code ...
undo.exec()
As a nice safety all exceptions from defer()ed calls are captured,
printed, and ignored (they do make the test fail, however).
This addresses the common problem of exceptions in cleanup paths
often being unhandled, leading to potential leaks.
There is a global action queue, flushed by ksft_run(). We could support
function level defers too, I guess, but there's no immediate need..
Link: https://lore.kernel.org/all/877cedb2ki.fsf@nvidia.com/ # [1]
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Reviewed-by: Petr Machata <petrm@nvidia.com>
Link: https://patch.msgid.link/20240627185502.3069139-3-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2024-06-27 11:55:01 -07:00
|
|
|
ksft_flush_defer()
|
|
|
|
|
2024-06-27 11:55:00 -07:00
|
|
|
if not cnt_key:
|
|
|
|
cnt_key = 'pass' if KSFT_RESULT else 'fail'
|
|
|
|
|
|
|
|
ktap_result(KSFT_RESULT, cnt, case, comment=comment)
|
|
|
|
totals[cnt_key] += 1
|
2024-04-04 19:45:22 -07:00
|
|
|
|
2024-07-04 18:52:22 -07:00
|
|
|
if stop:
|
|
|
|
break
|
|
|
|
|
2025-05-02 18:18:56 -07:00
|
|
|
signal.signal(signal.SIGTERM, prev_sigterm)
|
|
|
|
|
2024-04-04 19:45:22 -07:00
|
|
|
print(
|
|
|
|
f"# Totals: pass:{totals['pass']} fail:{totals['fail']} xfail:{totals['xfail']} xpass:0 skip:{totals['skip']} error:0"
|
|
|
|
)
|
2024-04-17 16:11:40 -07:00
|
|
|
|
|
|
|
|
|
|
|
def ksft_exit():
|
|
|
|
global KSFT_RESULT_ALL
|
|
|
|
sys.exit(0 if KSFT_RESULT_ALL else 1)
|