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