xref: /linux/tools/testing/selftests/net/lib/py/ksft.py (revision ef9226cd56b718c79184a3466d32984a51cb449c)
1# SPDX-License-Identifier: GPL-2.0
2
3import builtins
4import inspect
5import sys
6import time
7import traceback
8from .consts import KSFT_MAIN_NAME
9
10KSFT_RESULT = None
11KSFT_RESULT_ALL = True
12
13
14class KsftSkipEx(Exception):
15    pass
16
17
18class KsftXfailEx(Exception):
19    pass
20
21
22def ksft_pr(*objs, **kwargs):
23    print("#", *objs, **kwargs)
24
25
26def _fail(*args):
27    global KSFT_RESULT
28    KSFT_RESULT = False
29
30    frame = inspect.stack()[2]
31    ksft_pr("At " + frame.filename + " line " + str(frame.lineno) + ":")
32    ksft_pr(*args)
33
34
35def ksft_eq(a, b, comment=""):
36    global KSFT_RESULT
37    if a != b:
38        _fail("Check failed", a, "!=", b, comment)
39
40
41def ksft_true(a, comment=""):
42    if not a:
43        _fail("Check failed", a, "does not eval to True", comment)
44
45
46def ksft_in(a, b, comment=""):
47    if a not in b:
48        _fail("Check failed", a, "not in", b, comment)
49
50
51def ksft_ge(a, b, comment=""):
52    if a < b:
53        _fail("Check failed", a, "<", b, comment)
54
55
56class ksft_raises:
57    def __init__(self, expected_type):
58        self.exception = None
59        self.expected_type = expected_type
60
61    def __enter__(self):
62        return self
63
64    def __exit__(self, exc_type, exc_val, exc_tb):
65        if exc_type is None:
66            _fail(f"Expected exception {str(self.expected_type.__name__)}, none raised")
67        elif self.expected_type != exc_type:
68            _fail(f"Expected exception {str(self.expected_type.__name__)}, raised {str(exc_type.__name__)}")
69        self.exception = exc_val
70        # Suppress the exception if its the expected one
71        return self.expected_type == exc_type
72
73
74def ksft_busy_wait(cond, sleep=0.005, deadline=1, comment=""):
75    end = time.monotonic() + deadline
76    while True:
77        if cond():
78            return
79        if time.monotonic() > end:
80            _fail("Waiting for condition timed out", comment)
81            return
82        time.sleep(sleep)
83
84
85def ktap_result(ok, cnt=1, case="", comment=""):
86    global KSFT_RESULT_ALL
87    KSFT_RESULT_ALL = KSFT_RESULT_ALL and ok
88
89    res = ""
90    if not ok:
91        res += "not "
92    res += "ok "
93    res += str(cnt) + " "
94    res += KSFT_MAIN_NAME
95    if case:
96        res += "." + str(case.__name__)
97    if comment:
98        res += " # " + comment
99    print(res)
100
101
102def ksft_run(cases=None, globs=None, case_pfx=None, args=()):
103    cases = cases or []
104
105    if globs and case_pfx:
106        for key, value in globs.items():
107            if not callable(value):
108                continue
109            for prefix in case_pfx:
110                if key.startswith(prefix):
111                    cases.append(value)
112                    break
113
114    totals = {"pass": 0, "fail": 0, "skip": 0, "xfail": 0}
115
116    print("KTAP version 1")
117    print("1.." + str(len(cases)))
118
119    global KSFT_RESULT
120    cnt = 0
121    for case in cases:
122        KSFT_RESULT = True
123        cnt += 1
124        try:
125            case(*args)
126        except KsftSkipEx as e:
127            ktap_result(True, cnt, case, comment="SKIP " + str(e))
128            totals['skip'] += 1
129            continue
130        except KsftXfailEx as e:
131            ktap_result(True, cnt, case, comment="XFAIL " + str(e))
132            totals['xfail'] += 1
133            continue
134        except Exception as e:
135            tb = traceback.format_exc()
136            for line in tb.strip().split('\n'):
137                ksft_pr("Exception|", line)
138            ktap_result(False, cnt, case)
139            totals['fail'] += 1
140            continue
141
142        ktap_result(KSFT_RESULT, cnt, case)
143        if KSFT_RESULT:
144            totals['pass'] += 1
145        else:
146            totals['fail'] += 1
147
148    print(
149        f"# Totals: pass:{totals['pass']} fail:{totals['fail']} xfail:{totals['xfail']} xpass:0 skip:{totals['skip']} error:0"
150    )
151
152
153def ksft_exit():
154    global KSFT_RESULT_ALL
155    sys.exit(0 if KSFT_RESULT_ALL else 1)
156