1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# Copyright (C) 2013 Intel Corporation 3*4882a593Smuzhiyun# 4*4882a593Smuzhiyun# SPDX-License-Identifier: MIT 5*4882a593Smuzhiyun# 6*4882a593Smuzhiyun 7*4882a593Smuzhiyun# Main unittest module used by testimage.bbclass 8*4882a593Smuzhiyun# This provides the oeRuntimeTest base class which is inherited by all tests in meta/lib/oeqa/runtime. 9*4882a593Smuzhiyun 10*4882a593Smuzhiyun# It also has some helper functions and it's responsible for actually starting the tests 11*4882a593Smuzhiyun 12*4882a593Smuzhiyunimport os, re, sys 13*4882a593Smuzhiyunimport unittest 14*4882a593Smuzhiyunimport inspect 15*4882a593Smuzhiyunimport subprocess 16*4882a593Smuzhiyunimport signal 17*4882a593Smuzhiyunimport shutil 18*4882a593Smuzhiyunimport functools 19*4882a593Smuzhiyuntry: 20*4882a593Smuzhiyun import bb 21*4882a593Smuzhiyunexcept ImportError: 22*4882a593Smuzhiyun pass 23*4882a593Smuzhiyunimport logging 24*4882a593Smuzhiyun 25*4882a593Smuzhiyunimport oeqa.runtime 26*4882a593Smuzhiyun# Exported test doesn't require sdkext 27*4882a593Smuzhiyuntry: 28*4882a593Smuzhiyun import oeqa.sdkext 29*4882a593Smuzhiyunexcept ImportError: 30*4882a593Smuzhiyun pass 31*4882a593Smuzhiyunfrom oeqa.utils.decorators import LogResults, gettag, getResults 32*4882a593Smuzhiyun 33*4882a593Smuzhiyunlogger = logging.getLogger("BitBake") 34*4882a593Smuzhiyun 35*4882a593Smuzhiyundef getVar(obj): 36*4882a593Smuzhiyun #extend form dict, if a variable didn't exists, need find it in testcase 37*4882a593Smuzhiyun class VarDict(dict): 38*4882a593Smuzhiyun def __getitem__(self, key): 39*4882a593Smuzhiyun return gettag(obj, key) 40*4882a593Smuzhiyun return VarDict() 41*4882a593Smuzhiyun 42*4882a593Smuzhiyundef checkTags(tc, tagexp): 43*4882a593Smuzhiyun return eval(tagexp, None, getVar(tc)) 44*4882a593Smuzhiyun 45*4882a593Smuzhiyundef filterByTagExp(testsuite, tagexp): 46*4882a593Smuzhiyun if not tagexp: 47*4882a593Smuzhiyun return testsuite 48*4882a593Smuzhiyun caseList = [] 49*4882a593Smuzhiyun for each in testsuite: 50*4882a593Smuzhiyun if not isinstance(each, unittest.BaseTestSuite): 51*4882a593Smuzhiyun if checkTags(each, tagexp): 52*4882a593Smuzhiyun caseList.append(each) 53*4882a593Smuzhiyun else: 54*4882a593Smuzhiyun caseList.append(filterByTagExp(each, tagexp)) 55*4882a593Smuzhiyun return testsuite.__class__(caseList) 56*4882a593Smuzhiyun 57*4882a593Smuzhiyun@LogResults 58*4882a593Smuzhiyunclass oeTest(unittest.TestCase): 59*4882a593Smuzhiyun 60*4882a593Smuzhiyun pscmd = "ps" 61*4882a593Smuzhiyun longMessage = True 62*4882a593Smuzhiyun 63*4882a593Smuzhiyun @classmethod 64*4882a593Smuzhiyun def hasPackage(self, pkg): 65*4882a593Smuzhiyun """ 66*4882a593Smuzhiyun True if the full package name exists in the manifest, False otherwise. 67*4882a593Smuzhiyun """ 68*4882a593Smuzhiyun return pkg in oeTest.tc.pkgmanifest 69*4882a593Smuzhiyun 70*4882a593Smuzhiyun @classmethod 71*4882a593Smuzhiyun def hasPackageMatch(self, match): 72*4882a593Smuzhiyun """ 73*4882a593Smuzhiyun True if match exists in the manifest as a regular expression substring, 74*4882a593Smuzhiyun False otherwise. 75*4882a593Smuzhiyun """ 76*4882a593Smuzhiyun for s in oeTest.tc.pkgmanifest: 77*4882a593Smuzhiyun if re.match(match, s): 78*4882a593Smuzhiyun return True 79*4882a593Smuzhiyun return False 80*4882a593Smuzhiyun 81*4882a593Smuzhiyun @classmethod 82*4882a593Smuzhiyun def hasFeature(self,feature): 83*4882a593Smuzhiyun if feature in oeTest.tc.imagefeatures or \ 84*4882a593Smuzhiyun feature in oeTest.tc.distrofeatures: 85*4882a593Smuzhiyun return True 86*4882a593Smuzhiyun else: 87*4882a593Smuzhiyun return False 88*4882a593Smuzhiyun 89*4882a593Smuzhiyunclass oeRuntimeTest(oeTest): 90*4882a593Smuzhiyun def __init__(self, methodName='runTest'): 91*4882a593Smuzhiyun self.target = oeRuntimeTest.tc.target 92*4882a593Smuzhiyun super(oeRuntimeTest, self).__init__(methodName) 93*4882a593Smuzhiyun 94*4882a593Smuzhiyun def setUp(self): 95*4882a593Smuzhiyun # Install packages in the DUT 96*4882a593Smuzhiyun self.tc.install_uninstall_packages(self.id()) 97*4882a593Smuzhiyun 98*4882a593Smuzhiyun # Check if test needs to run 99*4882a593Smuzhiyun if self.tc.sigterm: 100*4882a593Smuzhiyun self.fail("Got SIGTERM") 101*4882a593Smuzhiyun elif (type(self.target).__name__ == "QemuTarget"): 102*4882a593Smuzhiyun self.assertTrue(self.target.check(), msg = "Qemu not running?") 103*4882a593Smuzhiyun 104*4882a593Smuzhiyun self.setUpLocal() 105*4882a593Smuzhiyun 106*4882a593Smuzhiyun # a setup method before tests but after the class instantiation 107*4882a593Smuzhiyun def setUpLocal(self): 108*4882a593Smuzhiyun pass 109*4882a593Smuzhiyun 110*4882a593Smuzhiyun def tearDown(self): 111*4882a593Smuzhiyun # Uninstall packages in the DUT 112*4882a593Smuzhiyun self.tc.install_uninstall_packages(self.id(), False) 113*4882a593Smuzhiyun 114*4882a593Smuzhiyun res = getResults() 115*4882a593Smuzhiyun # If a test fails or there is an exception dump 116*4882a593Smuzhiyun # for QemuTarget only 117*4882a593Smuzhiyun if (type(self.target).__name__ == "QemuTarget" and 118*4882a593Smuzhiyun (self.id() in res.getErrorList() or 119*4882a593Smuzhiyun self.id() in res.getFailList())): 120*4882a593Smuzhiyun self.tc.host_dumper.create_dir(self._testMethodName) 121*4882a593Smuzhiyun self.tc.host_dumper.dump_host() 122*4882a593Smuzhiyun self.target.target_dumper.dump_target( 123*4882a593Smuzhiyun self.tc.host_dumper.dump_dir) 124*4882a593Smuzhiyun print ("%s dump data stored in %s" % (self._testMethodName, 125*4882a593Smuzhiyun self.tc.host_dumper.dump_dir)) 126*4882a593Smuzhiyun 127*4882a593Smuzhiyun self.tearDownLocal() 128*4882a593Smuzhiyun 129*4882a593Smuzhiyun # Method to be run after tearDown and implemented by child classes 130*4882a593Smuzhiyun def tearDownLocal(self): 131*4882a593Smuzhiyun pass 132*4882a593Smuzhiyun 133*4882a593Smuzhiyundef getmodule(pos=2): 134*4882a593Smuzhiyun # stack returns a list of tuples containg frame information 135*4882a593Smuzhiyun # First element of the list the is current frame, caller is 1 136*4882a593Smuzhiyun frameinfo = inspect.stack()[pos] 137*4882a593Smuzhiyun modname = inspect.getmodulename(frameinfo[1]) 138*4882a593Smuzhiyun #modname = inspect.getmodule(frameinfo[0]).__name__ 139*4882a593Smuzhiyun return modname 140*4882a593Smuzhiyun 141*4882a593Smuzhiyundef skipModule(reason, pos=2): 142*4882a593Smuzhiyun modname = getmodule(pos) 143*4882a593Smuzhiyun if modname not in oeTest.tc.testsrequired: 144*4882a593Smuzhiyun raise unittest.SkipTest("%s: %s" % (modname, reason)) 145*4882a593Smuzhiyun else: 146*4882a593Smuzhiyun raise Exception("\nTest %s wants to be skipped.\nReason is: %s" \ 147*4882a593Smuzhiyun "\nTest was required in TEST_SUITES, so either the condition for skipping is wrong" \ 148*4882a593Smuzhiyun "\nor the image really doesn't have the required feature/package when it should." % (modname, reason)) 149*4882a593Smuzhiyun 150*4882a593Smuzhiyundef skipModuleIf(cond, reason): 151*4882a593Smuzhiyun 152*4882a593Smuzhiyun if cond: 153*4882a593Smuzhiyun skipModule(reason, 3) 154*4882a593Smuzhiyun 155*4882a593Smuzhiyundef skipModuleUnless(cond, reason): 156*4882a593Smuzhiyun 157*4882a593Smuzhiyun if not cond: 158*4882a593Smuzhiyun skipModule(reason, 3) 159*4882a593Smuzhiyun 160*4882a593Smuzhiyun_buffer_logger = "" 161*4882a593Smuzhiyundef custom_verbose(msg, *args, **kwargs): 162*4882a593Smuzhiyun global _buffer_logger 163*4882a593Smuzhiyun if msg[-1] != "\n": 164*4882a593Smuzhiyun _buffer_logger += msg 165*4882a593Smuzhiyun else: 166*4882a593Smuzhiyun _buffer_logger += msg 167*4882a593Smuzhiyun try: 168*4882a593Smuzhiyun bb.plain(_buffer_logger.rstrip("\n"), *args, **kwargs) 169*4882a593Smuzhiyun except NameError: 170*4882a593Smuzhiyun logger.info(_buffer_logger.rstrip("\n"), *args, **kwargs) 171*4882a593Smuzhiyun _buffer_logger = "" 172*4882a593Smuzhiyun 173*4882a593Smuzhiyunclass TestContext(object): 174*4882a593Smuzhiyun def __init__(self, d, exported=False): 175*4882a593Smuzhiyun self.d = d 176*4882a593Smuzhiyun 177*4882a593Smuzhiyun self.testsuites = self._get_test_suites() 178*4882a593Smuzhiyun 179*4882a593Smuzhiyun if exported: 180*4882a593Smuzhiyun path = [os.path.dirname(os.path.abspath(__file__))] 181*4882a593Smuzhiyun extrapath = "" 182*4882a593Smuzhiyun else: 183*4882a593Smuzhiyun path = d.getVar("BBPATH").split(':') 184*4882a593Smuzhiyun extrapath = "lib/oeqa" 185*4882a593Smuzhiyun 186*4882a593Smuzhiyun self.testslist = self._get_tests_list(path, extrapath) 187*4882a593Smuzhiyun self.testsrequired = self._get_test_suites_required() 188*4882a593Smuzhiyun 189*4882a593Smuzhiyun self.filesdir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "runtime/files") 190*4882a593Smuzhiyun self.corefilesdir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "files") 191*4882a593Smuzhiyun self.imagefeatures = d.getVar("IMAGE_FEATURES").split() 192*4882a593Smuzhiyun self.distrofeatures = d.getVar("DISTRO_FEATURES").split() 193*4882a593Smuzhiyun 194*4882a593Smuzhiyun # get testcase list from specified file 195*4882a593Smuzhiyun # if path is a relative path, then relative to build/conf/ 196*4882a593Smuzhiyun def _read_testlist(self, fpath, builddir): 197*4882a593Smuzhiyun if not os.path.isabs(fpath): 198*4882a593Smuzhiyun fpath = os.path.join(builddir, "conf", fpath) 199*4882a593Smuzhiyun if not os.path.exists(fpath): 200*4882a593Smuzhiyun bb.fatal("No such manifest file: ", fpath) 201*4882a593Smuzhiyun tcs = [] 202*4882a593Smuzhiyun for line in open(fpath).readlines(): 203*4882a593Smuzhiyun line = line.strip() 204*4882a593Smuzhiyun if line and not line.startswith("#"): 205*4882a593Smuzhiyun tcs.append(line) 206*4882a593Smuzhiyun return " ".join(tcs) 207*4882a593Smuzhiyun 208*4882a593Smuzhiyun # return test list by type also filter if TEST_SUITES is specified 209*4882a593Smuzhiyun def _get_tests_list(self, bbpath, extrapath): 210*4882a593Smuzhiyun testslist = [] 211*4882a593Smuzhiyun 212*4882a593Smuzhiyun type = self._get_test_namespace() 213*4882a593Smuzhiyun 214*4882a593Smuzhiyun # This relies on lib/ under each directory in BBPATH being added to sys.path 215*4882a593Smuzhiyun # (as done by default in base.bbclass) 216*4882a593Smuzhiyun for testname in self.testsuites: 217*4882a593Smuzhiyun if testname != "auto": 218*4882a593Smuzhiyun if testname.startswith("oeqa."): 219*4882a593Smuzhiyun testslist.append(testname) 220*4882a593Smuzhiyun continue 221*4882a593Smuzhiyun found = False 222*4882a593Smuzhiyun for p in bbpath: 223*4882a593Smuzhiyun if os.path.exists(os.path.join(p, extrapath, type, testname + ".py")): 224*4882a593Smuzhiyun testslist.append("oeqa." + type + "." + testname) 225*4882a593Smuzhiyun found = True 226*4882a593Smuzhiyun break 227*4882a593Smuzhiyun elif os.path.exists(os.path.join(p, extrapath, type, testname.split(".")[0] + ".py")): 228*4882a593Smuzhiyun testslist.append("oeqa." + type + "." + testname) 229*4882a593Smuzhiyun found = True 230*4882a593Smuzhiyun break 231*4882a593Smuzhiyun if not found: 232*4882a593Smuzhiyun bb.fatal('Test %s specified in TEST_SUITES could not be found in lib/oeqa/runtime under BBPATH' % testname) 233*4882a593Smuzhiyun 234*4882a593Smuzhiyun if "auto" in self.testsuites: 235*4882a593Smuzhiyun def add_auto_list(path): 236*4882a593Smuzhiyun files = sorted([f for f in os.listdir(path) if f.endswith('.py') and not f.startswith('_')]) 237*4882a593Smuzhiyun for f in files: 238*4882a593Smuzhiyun module = 'oeqa.' + type + '.' + f[:-3] 239*4882a593Smuzhiyun if module not in testslist: 240*4882a593Smuzhiyun testslist.append(module) 241*4882a593Smuzhiyun 242*4882a593Smuzhiyun for p in bbpath: 243*4882a593Smuzhiyun testpath = os.path.join(p, 'lib', 'oeqa', type) 244*4882a593Smuzhiyun bb.debug(2, 'Searching for tests in %s' % testpath) 245*4882a593Smuzhiyun if os.path.exists(testpath): 246*4882a593Smuzhiyun add_auto_list(testpath) 247*4882a593Smuzhiyun 248*4882a593Smuzhiyun return testslist 249*4882a593Smuzhiyun 250*4882a593Smuzhiyun def getTestModules(self): 251*4882a593Smuzhiyun """ 252*4882a593Smuzhiyun Returns all the test modules in the testlist. 253*4882a593Smuzhiyun """ 254*4882a593Smuzhiyun 255*4882a593Smuzhiyun import pkgutil 256*4882a593Smuzhiyun 257*4882a593Smuzhiyun modules = [] 258*4882a593Smuzhiyun for test in self.testslist: 259*4882a593Smuzhiyun if re.search("\w+\.\w+\.test_\S+", test): 260*4882a593Smuzhiyun test = '.'.join(t.split('.')[:3]) 261*4882a593Smuzhiyun module = pkgutil.get_loader(test) 262*4882a593Smuzhiyun modules.append(module) 263*4882a593Smuzhiyun 264*4882a593Smuzhiyun return modules 265*4882a593Smuzhiyun 266*4882a593Smuzhiyun def getModulefromID(self, test_id): 267*4882a593Smuzhiyun """ 268*4882a593Smuzhiyun Returns the test module based on a test id. 269*4882a593Smuzhiyun """ 270*4882a593Smuzhiyun 271*4882a593Smuzhiyun module_name = ".".join(test_id.split(".")[:3]) 272*4882a593Smuzhiyun modules = self.getTestModules() 273*4882a593Smuzhiyun for module in modules: 274*4882a593Smuzhiyun if module.name == module_name: 275*4882a593Smuzhiyun return module 276*4882a593Smuzhiyun 277*4882a593Smuzhiyun return None 278*4882a593Smuzhiyun 279*4882a593Smuzhiyun def getTests(self, test): 280*4882a593Smuzhiyun '''Return all individual tests executed when running the suite.''' 281*4882a593Smuzhiyun # Unfortunately unittest does not have an API for this, so we have 282*4882a593Smuzhiyun # to rely on implementation details. This only needs to work 283*4882a593Smuzhiyun # for TestSuite containing TestCase. 284*4882a593Smuzhiyun method = getattr(test, '_testMethodName', None) 285*4882a593Smuzhiyun if method: 286*4882a593Smuzhiyun # leaf case: a TestCase 287*4882a593Smuzhiyun yield test 288*4882a593Smuzhiyun else: 289*4882a593Smuzhiyun # Look into TestSuite. 290*4882a593Smuzhiyun tests = getattr(test, '_tests', []) 291*4882a593Smuzhiyun for t1 in tests: 292*4882a593Smuzhiyun for t2 in self.getTests(t1): 293*4882a593Smuzhiyun yield t2 294*4882a593Smuzhiyun 295*4882a593Smuzhiyun def loadTests(self): 296*4882a593Smuzhiyun setattr(oeTest, "tc", self) 297*4882a593Smuzhiyun 298*4882a593Smuzhiyun testloader = unittest.TestLoader() 299*4882a593Smuzhiyun testloader.sortTestMethodsUsing = None 300*4882a593Smuzhiyun suites = [testloader.loadTestsFromName(name) for name in self.testslist] 301*4882a593Smuzhiyun suites = filterByTagExp(suites, getattr(self, "tagexp", None)) 302*4882a593Smuzhiyun 303*4882a593Smuzhiyun # Determine dependencies between suites by looking for @skipUnlessPassed 304*4882a593Smuzhiyun # method annotations. Suite A depends on suite B if any method in A 305*4882a593Smuzhiyun # depends on a method on B. 306*4882a593Smuzhiyun for suite in suites: 307*4882a593Smuzhiyun suite.dependencies = [] 308*4882a593Smuzhiyun suite.depth = 0 309*4882a593Smuzhiyun for test in self.getTests(suite): 310*4882a593Smuzhiyun methodname = getattr(test, '_testMethodName', None) 311*4882a593Smuzhiyun if methodname: 312*4882a593Smuzhiyun method = getattr(test, methodname) 313*4882a593Smuzhiyun depends_on = getattr(method, '_depends_on', None) 314*4882a593Smuzhiyun if depends_on: 315*4882a593Smuzhiyun for dep_suite in suites: 316*4882a593Smuzhiyun if depends_on in [getattr(t, '_testMethodName', None) for t in self.getTests(dep_suite)]: 317*4882a593Smuzhiyun if dep_suite not in suite.dependencies and \ 318*4882a593Smuzhiyun dep_suite is not suite: 319*4882a593Smuzhiyun suite.dependencies.append(dep_suite) 320*4882a593Smuzhiyun break 321*4882a593Smuzhiyun else: 322*4882a593Smuzhiyun logger.warning("Test %s was declared as @skipUnlessPassed('%s') but that test is either not defined or not active. Will run the test anyway." % 323*4882a593Smuzhiyun (test, depends_on)) 324*4882a593Smuzhiyun 325*4882a593Smuzhiyun # Use brute-force topological sort to determine ordering. Sort by 326*4882a593Smuzhiyun # depth (higher depth = must run later), with original ordering to 327*4882a593Smuzhiyun # break ties. 328*4882a593Smuzhiyun def set_suite_depth(suite): 329*4882a593Smuzhiyun for dep in suite.dependencies: 330*4882a593Smuzhiyun new_depth = set_suite_depth(dep) + 1 331*4882a593Smuzhiyun if new_depth > suite.depth: 332*4882a593Smuzhiyun suite.depth = new_depth 333*4882a593Smuzhiyun return suite.depth 334*4882a593Smuzhiyun 335*4882a593Smuzhiyun for index, suite in enumerate(suites): 336*4882a593Smuzhiyun set_suite_depth(suite) 337*4882a593Smuzhiyun suite.index = index 338*4882a593Smuzhiyun 339*4882a593Smuzhiyun def cmp(a, b): 340*4882a593Smuzhiyun return (a > b) - (a < b) 341*4882a593Smuzhiyun 342*4882a593Smuzhiyun def cmpfunc(a, b): 343*4882a593Smuzhiyun return cmp((a.depth, a.index), (b.depth, b.index)) 344*4882a593Smuzhiyun 345*4882a593Smuzhiyun suites.sort(key=functools.cmp_to_key(cmpfunc)) 346*4882a593Smuzhiyun 347*4882a593Smuzhiyun self.suite = testloader.suiteClass(suites) 348*4882a593Smuzhiyun 349*4882a593Smuzhiyun return self.suite 350*4882a593Smuzhiyun 351*4882a593Smuzhiyun def runTests(self): 352*4882a593Smuzhiyun logger.info("Test modules %s" % self.testslist) 353*4882a593Smuzhiyun if hasattr(self, "tagexp") and self.tagexp: 354*4882a593Smuzhiyun logger.info("Filter test cases by tags: %s" % self.tagexp) 355*4882a593Smuzhiyun logger.info("Found %s tests" % self.suite.countTestCases()) 356*4882a593Smuzhiyun runner = unittest.TextTestRunner(verbosity=2) 357*4882a593Smuzhiyun if 'bb' in sys.modules: 358*4882a593Smuzhiyun runner.stream.write = custom_verbose 359*4882a593Smuzhiyun 360*4882a593Smuzhiyun return runner.run(self.suite) 361*4882a593Smuzhiyun 362*4882a593Smuzhiyunclass RuntimeTestContext(TestContext): 363*4882a593Smuzhiyun def __init__(self, d, target, exported=False): 364*4882a593Smuzhiyun super(RuntimeTestContext, self).__init__(d, exported) 365*4882a593Smuzhiyun 366*4882a593Smuzhiyun self.target = target 367*4882a593Smuzhiyun 368*4882a593Smuzhiyun self.pkgmanifest = {} 369*4882a593Smuzhiyun manifest = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), 370*4882a593Smuzhiyun d.getVar("IMAGE_LINK_NAME") + ".manifest") 371*4882a593Smuzhiyun nomanifest = d.getVar("IMAGE_NO_MANIFEST") 372*4882a593Smuzhiyun if nomanifest is None or nomanifest != "1": 373*4882a593Smuzhiyun try: 374*4882a593Smuzhiyun with open(manifest) as f: 375*4882a593Smuzhiyun for line in f: 376*4882a593Smuzhiyun (pkg, arch, version) = line.strip().split() 377*4882a593Smuzhiyun self.pkgmanifest[pkg] = (version, arch) 378*4882a593Smuzhiyun except IOError as e: 379*4882a593Smuzhiyun bb.fatal("No package manifest file found. Did you build the image?\n%s" % e) 380*4882a593Smuzhiyun 381*4882a593Smuzhiyun def _get_test_namespace(self): 382*4882a593Smuzhiyun return "runtime" 383*4882a593Smuzhiyun 384*4882a593Smuzhiyun def _get_test_suites(self): 385*4882a593Smuzhiyun testsuites = [] 386*4882a593Smuzhiyun 387*4882a593Smuzhiyun manifests = (self.d.getVar("TEST_SUITES_MANIFEST") or '').split() 388*4882a593Smuzhiyun if manifests: 389*4882a593Smuzhiyun for manifest in manifests: 390*4882a593Smuzhiyun testsuites.extend(self._read_testlist(manifest, 391*4882a593Smuzhiyun self.d.getVar("TOPDIR")).split()) 392*4882a593Smuzhiyun 393*4882a593Smuzhiyun else: 394*4882a593Smuzhiyun testsuites = self.d.getVar("TEST_SUITES").split() 395*4882a593Smuzhiyun 396*4882a593Smuzhiyun return testsuites 397*4882a593Smuzhiyun 398*4882a593Smuzhiyun def _get_test_suites_required(self): 399*4882a593Smuzhiyun return [t for t in self.d.getVar("TEST_SUITES").split() if t != "auto"] 400*4882a593Smuzhiyun 401*4882a593Smuzhiyun def loadTests(self): 402*4882a593Smuzhiyun super(RuntimeTestContext, self).loadTests() 403*4882a593Smuzhiyun if oeTest.hasPackage("procps"): 404*4882a593Smuzhiyun oeRuntimeTest.pscmd = "ps -ef" 405*4882a593Smuzhiyun 406*4882a593Smuzhiyun def extract_packages(self): 407*4882a593Smuzhiyun """ 408*4882a593Smuzhiyun Find packages that will be needed during runtime. 409*4882a593Smuzhiyun """ 410*4882a593Smuzhiyun 411*4882a593Smuzhiyun modules = self.getTestModules() 412*4882a593Smuzhiyun bbpaths = self.d.getVar("BBPATH").split(":") 413*4882a593Smuzhiyun 414*4882a593Smuzhiyun shutil.rmtree(self.d.getVar("TEST_EXTRACTED_DIR")) 415*4882a593Smuzhiyun shutil.rmtree(self.d.getVar("TEST_PACKAGED_DIR")) 416*4882a593Smuzhiyun for module in modules: 417*4882a593Smuzhiyun json_file = self._getJsonFile(module) 418*4882a593Smuzhiyun if json_file: 419*4882a593Smuzhiyun needed_packages = self._getNeededPackages(json_file) 420*4882a593Smuzhiyun self._perform_package_extraction(needed_packages) 421*4882a593Smuzhiyun 422*4882a593Smuzhiyun def _perform_package_extraction(self, needed_packages): 423*4882a593Smuzhiyun """ 424*4882a593Smuzhiyun Extract packages that will be needed during runtime. 425*4882a593Smuzhiyun """ 426*4882a593Smuzhiyun 427*4882a593Smuzhiyun import oe.path 428*4882a593Smuzhiyun 429*4882a593Smuzhiyun extracted_path = self.d.getVar("TEST_EXTRACTED_DIR") 430*4882a593Smuzhiyun packaged_path = self.d.getVar("TEST_PACKAGED_DIR") 431*4882a593Smuzhiyun 432*4882a593Smuzhiyun for key,value in needed_packages.items(): 433*4882a593Smuzhiyun packages = () 434*4882a593Smuzhiyun if isinstance(value, dict): 435*4882a593Smuzhiyun packages = (value, ) 436*4882a593Smuzhiyun elif isinstance(value, list): 437*4882a593Smuzhiyun packages = value 438*4882a593Smuzhiyun else: 439*4882a593Smuzhiyun bb.fatal("Failed to process needed packages for %s; " 440*4882a593Smuzhiyun "Value must be a dict or list" % key) 441*4882a593Smuzhiyun 442*4882a593Smuzhiyun for package in packages: 443*4882a593Smuzhiyun pkg = package["pkg"] 444*4882a593Smuzhiyun rm = package.get("rm", False) 445*4882a593Smuzhiyun extract = package.get("extract", True) 446*4882a593Smuzhiyun if extract: 447*4882a593Smuzhiyun dst_dir = os.path.join(extracted_path, pkg) 448*4882a593Smuzhiyun else: 449*4882a593Smuzhiyun dst_dir = os.path.join(packaged_path) 450*4882a593Smuzhiyun 451*4882a593Smuzhiyun # Extract package and copy it to TEST_EXTRACTED_DIR 452*4882a593Smuzhiyun pkg_dir = self._extract_in_tmpdir(pkg) 453*4882a593Smuzhiyun if extract: 454*4882a593Smuzhiyun 455*4882a593Smuzhiyun # Same package used for more than one test, 456*4882a593Smuzhiyun # don't need to extract again. 457*4882a593Smuzhiyun if os.path.exists(dst_dir): 458*4882a593Smuzhiyun continue 459*4882a593Smuzhiyun oe.path.copytree(pkg_dir, dst_dir) 460*4882a593Smuzhiyun shutil.rmtree(pkg_dir) 461*4882a593Smuzhiyun 462*4882a593Smuzhiyun # Copy package to TEST_PACKAGED_DIR 463*4882a593Smuzhiyun else: 464*4882a593Smuzhiyun self._copy_package(pkg) 465*4882a593Smuzhiyun 466*4882a593Smuzhiyun def _getJsonFile(self, module): 467*4882a593Smuzhiyun """ 468*4882a593Smuzhiyun Returns the path of the JSON file for a module, empty if doesn't exitst. 469*4882a593Smuzhiyun """ 470*4882a593Smuzhiyun 471*4882a593Smuzhiyun module_file = module.path 472*4882a593Smuzhiyun json_file = "%s.json" % module_file.rsplit(".", 1)[0] 473*4882a593Smuzhiyun if os.path.isfile(module_file) and os.path.isfile(json_file): 474*4882a593Smuzhiyun return json_file 475*4882a593Smuzhiyun else: 476*4882a593Smuzhiyun return "" 477*4882a593Smuzhiyun 478*4882a593Smuzhiyun def _getNeededPackages(self, json_file, test=None): 479*4882a593Smuzhiyun """ 480*4882a593Smuzhiyun Returns a dict with needed packages based on a JSON file. 481*4882a593Smuzhiyun 482*4882a593Smuzhiyun 483*4882a593Smuzhiyun If a test is specified it will return the dict just for that test. 484*4882a593Smuzhiyun """ 485*4882a593Smuzhiyun 486*4882a593Smuzhiyun import json 487*4882a593Smuzhiyun 488*4882a593Smuzhiyun needed_packages = {} 489*4882a593Smuzhiyun 490*4882a593Smuzhiyun with open(json_file) as f: 491*4882a593Smuzhiyun test_packages = json.load(f) 492*4882a593Smuzhiyun for key,value in test_packages.items(): 493*4882a593Smuzhiyun needed_packages[key] = value 494*4882a593Smuzhiyun 495*4882a593Smuzhiyun if test: 496*4882a593Smuzhiyun if test in needed_packages: 497*4882a593Smuzhiyun needed_packages = needed_packages[test] 498*4882a593Smuzhiyun else: 499*4882a593Smuzhiyun needed_packages = {} 500*4882a593Smuzhiyun 501*4882a593Smuzhiyun return needed_packages 502*4882a593Smuzhiyun 503*4882a593Smuzhiyun def _extract_in_tmpdir(self, pkg): 504*4882a593Smuzhiyun """" 505*4882a593Smuzhiyun Returns path to a temp directory where the package was 506*4882a593Smuzhiyun extracted without dependencies. 507*4882a593Smuzhiyun """ 508*4882a593Smuzhiyun 509*4882a593Smuzhiyun from oeqa.utils.package_manager import get_package_manager 510*4882a593Smuzhiyun 511*4882a593Smuzhiyun pkg_path = os.path.join(self.d.getVar("TEST_INSTALL_TMP_DIR"), pkg) 512*4882a593Smuzhiyun pm = get_package_manager(self.d, pkg_path) 513*4882a593Smuzhiyun extract_dir = pm.extract(pkg) 514*4882a593Smuzhiyun shutil.rmtree(pkg_path) 515*4882a593Smuzhiyun 516*4882a593Smuzhiyun return extract_dir 517*4882a593Smuzhiyun 518*4882a593Smuzhiyun def _copy_package(self, pkg): 519*4882a593Smuzhiyun """ 520*4882a593Smuzhiyun Copy the RPM, DEB or IPK package to dst_dir 521*4882a593Smuzhiyun """ 522*4882a593Smuzhiyun 523*4882a593Smuzhiyun from oeqa.utils.package_manager import get_package_manager 524*4882a593Smuzhiyun 525*4882a593Smuzhiyun pkg_path = os.path.join(self.d.getVar("TEST_INSTALL_TMP_DIR"), pkg) 526*4882a593Smuzhiyun dst_dir = self.d.getVar("TEST_PACKAGED_DIR") 527*4882a593Smuzhiyun pm = get_package_manager(self.d, pkg_path) 528*4882a593Smuzhiyun pkg_info = pm.package_info(pkg) 529*4882a593Smuzhiyun file_path = pkg_info[pkg]["filepath"] 530*4882a593Smuzhiyun shutil.copy2(file_path, dst_dir) 531*4882a593Smuzhiyun shutil.rmtree(pkg_path) 532*4882a593Smuzhiyun 533*4882a593Smuzhiyun def install_uninstall_packages(self, test_id, pkg_dir, install): 534*4882a593Smuzhiyun """ 535*4882a593Smuzhiyun Check if the test requires a package and Install/Uninstall it in the DUT 536*4882a593Smuzhiyun """ 537*4882a593Smuzhiyun 538*4882a593Smuzhiyun test = test_id.split(".")[4] 539*4882a593Smuzhiyun module = self.getModulefromID(test_id) 540*4882a593Smuzhiyun json = self._getJsonFile(module) 541*4882a593Smuzhiyun if json: 542*4882a593Smuzhiyun needed_packages = self._getNeededPackages(json, test) 543*4882a593Smuzhiyun if needed_packages: 544*4882a593Smuzhiyun self._install_uninstall_packages(needed_packages, pkg_dir, install) 545*4882a593Smuzhiyun 546*4882a593Smuzhiyun def _install_uninstall_packages(self, needed_packages, pkg_dir, install=True): 547*4882a593Smuzhiyun """ 548*4882a593Smuzhiyun Install/Uninstall packages in the DUT without using a package manager 549*4882a593Smuzhiyun """ 550*4882a593Smuzhiyun 551*4882a593Smuzhiyun if isinstance(needed_packages, dict): 552*4882a593Smuzhiyun packages = [needed_packages] 553*4882a593Smuzhiyun elif isinstance(needed_packages, list): 554*4882a593Smuzhiyun packages = needed_packages 555*4882a593Smuzhiyun 556*4882a593Smuzhiyun for package in packages: 557*4882a593Smuzhiyun pkg = package["pkg"] 558*4882a593Smuzhiyun rm = package.get("rm", False) 559*4882a593Smuzhiyun extract = package.get("extract", True) 560*4882a593Smuzhiyun src_dir = os.path.join(pkg_dir, pkg) 561*4882a593Smuzhiyun 562*4882a593Smuzhiyun # Install package 563*4882a593Smuzhiyun if install and extract: 564*4882a593Smuzhiyun self.target.connection.copy_dir_to(src_dir, "/") 565*4882a593Smuzhiyun 566*4882a593Smuzhiyun # Uninstall package 567*4882a593Smuzhiyun elif not install and rm: 568*4882a593Smuzhiyun self.target.connection.delete_dir_structure(src_dir, "/") 569*4882a593Smuzhiyun 570*4882a593Smuzhiyunclass ImageTestContext(RuntimeTestContext): 571*4882a593Smuzhiyun def __init__(self, d, target, host_dumper): 572*4882a593Smuzhiyun super(ImageTestContext, self).__init__(d, target) 573*4882a593Smuzhiyun 574*4882a593Smuzhiyun self.tagexp = d.getVar("TEST_SUITES_TAGS") 575*4882a593Smuzhiyun 576*4882a593Smuzhiyun self.host_dumper = host_dumper 577*4882a593Smuzhiyun 578*4882a593Smuzhiyun self.sigterm = False 579*4882a593Smuzhiyun self.origsigtermhandler = signal.getsignal(signal.SIGTERM) 580*4882a593Smuzhiyun signal.signal(signal.SIGTERM, self._sigterm_exception) 581*4882a593Smuzhiyun 582*4882a593Smuzhiyun def _sigterm_exception(self, signum, stackframe): 583*4882a593Smuzhiyun bb.warn("TestImage received SIGTERM, shutting down...") 584*4882a593Smuzhiyun self.sigterm = True 585*4882a593Smuzhiyun self.target.stop() 586*4882a593Smuzhiyun 587*4882a593Smuzhiyun def install_uninstall_packages(self, test_id, install=True): 588*4882a593Smuzhiyun """ 589*4882a593Smuzhiyun Check if the test requires a package and Install/Uninstall it in the DUT 590*4882a593Smuzhiyun """ 591*4882a593Smuzhiyun 592*4882a593Smuzhiyun pkg_dir = self.d.getVar("TEST_EXTRACTED_DIR") 593*4882a593Smuzhiyun super(ImageTestContext, self).install_uninstall_packages(test_id, pkg_dir, install) 594*4882a593Smuzhiyun 595*4882a593Smuzhiyunclass ExportTestContext(RuntimeTestContext): 596*4882a593Smuzhiyun def __init__(self, d, target, exported=False, parsedArgs={}): 597*4882a593Smuzhiyun """ 598*4882a593Smuzhiyun This class is used when exporting tests and when are executed outside OE environment. 599*4882a593Smuzhiyun 600*4882a593Smuzhiyun parsedArgs can contain the following: 601*4882a593Smuzhiyun - tag: Filter test by tag. 602*4882a593Smuzhiyun """ 603*4882a593Smuzhiyun super(ExportTestContext, self).__init__(d, target, exported) 604*4882a593Smuzhiyun 605*4882a593Smuzhiyun tag = parsedArgs.get("tag", None) 606*4882a593Smuzhiyun self.tagexp = tag if tag != None else d.getVar("TEST_SUITES_TAGS") 607*4882a593Smuzhiyun 608*4882a593Smuzhiyun self.sigterm = None 609*4882a593Smuzhiyun 610*4882a593Smuzhiyun def install_uninstall_packages(self, test_id, install=True): 611*4882a593Smuzhiyun """ 612*4882a593Smuzhiyun Check if the test requires a package and Install/Uninstall it in the DUT 613*4882a593Smuzhiyun """ 614*4882a593Smuzhiyun 615*4882a593Smuzhiyun export_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 616*4882a593Smuzhiyun extracted_dir = self.d.getVar("TEST_EXPORT_EXTRACTED_DIR") 617*4882a593Smuzhiyun pkg_dir = os.path.join(export_dir, extracted_dir) 618*4882a593Smuzhiyun super(ExportTestContext, self).install_uninstall_packages(test_id, pkg_dir, install) 619