xref: /OK3568_Linux_fs/yocto/poky/meta/lib/oeqa/oetest.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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