xref: /aosp_15_r20/external/bcc/tests/python/utils.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1*387f9dfdSAndroid Build Coastguard Workerfrom pyroute2 import NSPopen
2*387f9dfdSAndroid Build Coastguard Workerimport traceback
3*387f9dfdSAndroid Build Coastguard Workerimport shutil
4*387f9dfdSAndroid Build Coastguard Worker
5*387f9dfdSAndroid Build Coastguard Workerimport logging, os, sys, re
6*387f9dfdSAndroid Build Coastguard Worker
7*387f9dfdSAndroid Build Coastguard Workerif 'PYTHON_TEST_LOGFILE' in os.environ:
8*387f9dfdSAndroid Build Coastguard Worker    logfile=os.environ['PYTHON_TEST_LOGFILE']
9*387f9dfdSAndroid Build Coastguard Worker    logging.basicConfig(level=logging.ERROR, filename=logfile, filemode='a')
10*387f9dfdSAndroid Build Coastguard Workerelse:
11*387f9dfdSAndroid Build Coastguard Worker    logging.basicConfig(level=logging.ERROR, stream=sys.stderr)
12*387f9dfdSAndroid Build Coastguard Worker
13*387f9dfdSAndroid Build Coastguard Workerlogger = logging.getLogger()
14*387f9dfdSAndroid Build Coastguard Worker
15*387f9dfdSAndroid Build Coastguard Workerdef has_executable(name):
16*387f9dfdSAndroid Build Coastguard Worker    path = shutil.which(name)
17*387f9dfdSAndroid Build Coastguard Worker    if path is None:
18*387f9dfdSAndroid Build Coastguard Worker        raise Exception(name + ": command not found")
19*387f9dfdSAndroid Build Coastguard Worker    return path
20*387f9dfdSAndroid Build Coastguard Worker
21*387f9dfdSAndroid Build Coastguard Worker# This is a decorator that will allow for logging tests, but flagging them as
22*387f9dfdSAndroid Build Coastguard Worker# "known to fail". These tests legitimately fail and represent actual bugs, but
23*387f9dfdSAndroid Build Coastguard Worker# as these are already documented the test status can be "green" without these
24*387f9dfdSAndroid Build Coastguard Worker# tests, similar to catch2's [!mayfail] tag.
25*387f9dfdSAndroid Build Coastguard Worker# This is done using the existing python unittest concept of an "expected failure",
26*387f9dfdSAndroid Build Coastguard Worker# but it is only done after the fact, if the test fails or raises an exception.
27*387f9dfdSAndroid Build Coastguard Worker# It gives all tests a chance to succeed, but if they fail it logs them and
28*387f9dfdSAndroid Build Coastguard Worker# continues.
29*387f9dfdSAndroid Build Coastguard Workerdef mayFail(message):
30*387f9dfdSAndroid Build Coastguard Worker    def decorator(func):
31*387f9dfdSAndroid Build Coastguard Worker        def wrapper(*args, **kwargs):
32*387f9dfdSAndroid Build Coastguard Worker            res = None
33*387f9dfdSAndroid Build Coastguard Worker            err = None
34*387f9dfdSAndroid Build Coastguard Worker            try:
35*387f9dfdSAndroid Build Coastguard Worker                res = func(*args, **kwargs)
36*387f9dfdSAndroid Build Coastguard Worker            except BaseException as e:
37*387f9dfdSAndroid Build Coastguard Worker                logger.critical("WARNING! Test %s failed, but marked as passed because it is decorated with @mayFail." %
38*387f9dfdSAndroid Build Coastguard Worker                       args[0])
39*387f9dfdSAndroid Build Coastguard Worker                logger.critical("\tThe reason why this mayFail was: %s" % message)
40*387f9dfdSAndroid Build Coastguard Worker                logger.critical("\tThe failure was: \"%s\"" % e)
41*387f9dfdSAndroid Build Coastguard Worker                logger.critical("\tStacktrace: \"%s\"" % traceback.format_exc())
42*387f9dfdSAndroid Build Coastguard Worker                testcase=args[0]
43*387f9dfdSAndroid Build Coastguard Worker                testcase.TestResult().addExpectedFailure(testcase, e)
44*387f9dfdSAndroid Build Coastguard Worker                err = e
45*387f9dfdSAndroid Build Coastguard Worker            finally:
46*387f9dfdSAndroid Build Coastguard Worker                if err != None:
47*387f9dfdSAndroid Build Coastguard Worker                    raise err
48*387f9dfdSAndroid Build Coastguard Worker                else:
49*387f9dfdSAndroid Build Coastguard Worker                    return res
50*387f9dfdSAndroid Build Coastguard Worker        return wrapper
51*387f9dfdSAndroid Build Coastguard Worker    return decorator
52*387f9dfdSAndroid Build Coastguard Worker
53*387f9dfdSAndroid Build Coastguard Worker# This is a decorator that will skip tests if any binary in the list is not in PATH.
54*387f9dfdSAndroid Build Coastguard Workerdef skipUnlessHasBinaries(binaries, message):
55*387f9dfdSAndroid Build Coastguard Worker    def decorator(func):
56*387f9dfdSAndroid Build Coastguard Worker        def wrapper(self, *args, **kwargs):
57*387f9dfdSAndroid Build Coastguard Worker            missing = []
58*387f9dfdSAndroid Build Coastguard Worker            for binary in binaries:
59*387f9dfdSAndroid Build Coastguard Worker                if shutil.which(binary) is None:
60*387f9dfdSAndroid Build Coastguard Worker                    missing.append(binary)
61*387f9dfdSAndroid Build Coastguard Worker
62*387f9dfdSAndroid Build Coastguard Worker            if len(missing):
63*387f9dfdSAndroid Build Coastguard Worker                missing_binaries = ", ".join(missing)
64*387f9dfdSAndroid Build Coastguard Worker                self.skipTest(f"Missing binaries: {missing_binaries}. {message}")
65*387f9dfdSAndroid Build Coastguard Worker            else:
66*387f9dfdSAndroid Build Coastguard Worker                func(self, *args, **kwargs)
67*387f9dfdSAndroid Build Coastguard Worker        return wrapper
68*387f9dfdSAndroid Build Coastguard Worker    return decorator
69*387f9dfdSAndroid Build Coastguard Worker
70*387f9dfdSAndroid Build Coastguard Workerclass NSPopenWithCheck(NSPopen):
71*387f9dfdSAndroid Build Coastguard Worker    """
72*387f9dfdSAndroid Build Coastguard Worker    A wrapper for NSPopen that additionally checks if the program
73*387f9dfdSAndroid Build Coastguard Worker    to be executed is available from the system path or not.
74*387f9dfdSAndroid Build Coastguard Worker    If found, it proceeds with the usual NSPopen() call.
75*387f9dfdSAndroid Build Coastguard Worker    Otherwise, it raises an exception.
76*387f9dfdSAndroid Build Coastguard Worker    """
77*387f9dfdSAndroid Build Coastguard Worker
78*387f9dfdSAndroid Build Coastguard Worker    def __init__(self, nsname, *argv, **kwarg):
79*387f9dfdSAndroid Build Coastguard Worker        name = list(argv)[0][0]
80*387f9dfdSAndroid Build Coastguard Worker        has_executable(name)
81*387f9dfdSAndroid Build Coastguard Worker        super(NSPopenWithCheck, self).__init__(nsname, *argv, **kwarg)
82*387f9dfdSAndroid Build Coastguard Worker
83*387f9dfdSAndroid Build Coastguard WorkerKERNEL_VERSION_PATTERN = r"v?(?P<major>[0-9]+)\.(?P<minor>[0-9]+).*"
84*387f9dfdSAndroid Build Coastguard Worker
85*387f9dfdSAndroid Build Coastguard Workerdef kernel_version_ge(major, minor):
86*387f9dfdSAndroid Build Coastguard Worker    # True if running kernel is >= X.Y
87*387f9dfdSAndroid Build Coastguard Worker    match = re.match(KERNEL_VERSION_PATTERN, os.uname()[2])
88*387f9dfdSAndroid Build Coastguard Worker    x = int(match.group("major"))
89*387f9dfdSAndroid Build Coastguard Worker    y = int(match.group("minor"))
90*387f9dfdSAndroid Build Coastguard Worker    if x > major:
91*387f9dfdSAndroid Build Coastguard Worker        return True
92*387f9dfdSAndroid Build Coastguard Worker    if x < major:
93*387f9dfdSAndroid Build Coastguard Worker        return False
94*387f9dfdSAndroid Build Coastguard Worker    if minor and y < minor:
95*387f9dfdSAndroid Build Coastguard Worker        return False
96*387f9dfdSAndroid Build Coastguard Worker    return True
97