blob: 3ed5bb8c2b31becd041f5fd824ecdcdb0093ae0f [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# Copyright (C) 2013 Intel Corporation
2#
3# Released under the MIT license (see COPYING.MIT)
4
5# Main unittest module used by testimage.bbclass
6# This provides the oeRuntimeTest base class which is inherited by all tests in meta/lib/oeqa/runtime.
7
8# It also has some helper functions and it's responsible for actually starting the tests
9
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050010import os, re, mmap, sys
Patrick Williamsc124f4f2015-09-15 14:41:29 -050011import unittest
12import inspect
13import subprocess
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050014import signal
Patrick Williamsf1e5d692016-03-30 15:21:19 -050015try:
16 import bb
17except ImportError:
18 pass
19import logging
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050020
21import oeqa.runtime
22# Exported test doesn't require sdkext
23try:
24 import oeqa.sdkext
25except ImportError:
26 pass
Patrick Williamsf1e5d692016-03-30 15:21:19 -050027from oeqa.utils.decorators import LogResults, gettag, getResults
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050028from oeqa.utils import avoid_paths_in_environ
Patrick Williamsf1e5d692016-03-30 15:21:19 -050029
30logger = logging.getLogger("BitBake")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050031
32def getVar(obj):
33 #extend form dict, if a variable didn't exists, need find it in testcase
34 class VarDict(dict):
35 def __getitem__(self, key):
36 return gettag(obj, key)
37 return VarDict()
38
39def checkTags(tc, tagexp):
40 return eval(tagexp, None, getVar(tc))
41
Patrick Williamsc124f4f2015-09-15 14:41:29 -050042def filterByTagExp(testsuite, tagexp):
43 if not tagexp:
44 return testsuite
45 caseList = []
46 for each in testsuite:
47 if not isinstance(each, unittest.BaseTestSuite):
48 if checkTags(each, tagexp):
49 caseList.append(each)
50 else:
51 caseList.append(filterByTagExp(each, tagexp))
52 return testsuite.__class__(caseList)
53
Patrick Williamsc124f4f2015-09-15 14:41:29 -050054@LogResults
55class oeTest(unittest.TestCase):
56
57 longMessage = True
58
59 @classmethod
60 def hasPackage(self, pkg):
61 for item in oeTest.tc.pkgmanifest.split('\n'):
62 if re.match(pkg, item):
63 return True
64 return False
65
66 @classmethod
67 def hasFeature(self,feature):
68
69 if feature in oeTest.tc.imagefeatures or \
70 feature in oeTest.tc.distrofeatures:
71 return True
72 else:
73 return False
74
75class oeRuntimeTest(oeTest):
76 def __init__(self, methodName='runTest'):
77 self.target = oeRuntimeTest.tc.target
78 super(oeRuntimeTest, self).__init__(methodName)
79
80 def setUp(self):
81 # Check if test needs to run
82 if self.tc.sigterm:
83 self.fail("Got SIGTERM")
84 elif (type(self.target).__name__ == "QemuTarget"):
85 self.assertTrue(self.target.check(), msg = "Qemu not running?")
86
Patrick Williamsd7e96312015-09-22 08:09:05 -050087 self.setUpLocal()
88
89 # a setup method before tests but after the class instantiation
90 def setUpLocal(self):
91 pass
92
Patrick Williamsc124f4f2015-09-15 14:41:29 -050093 def tearDown(self):
Patrick Williamsf1e5d692016-03-30 15:21:19 -050094 res = getResults()
95 # If a test fails or there is an exception dump
96 # for QemuTarget only
97 if (type(self.target).__name__ == "QemuTarget" and
98 (self.id() in res.getErrorList() or
99 self.id() in res.getFailList())):
100 self.tc.host_dumper.create_dir(self._testMethodName)
101 self.tc.host_dumper.dump_host()
102 self.target.target_dumper.dump_target(
103 self.tc.host_dumper.dump_dir)
104 print ("%s dump data stored in %s" % (self._testMethodName,
105 self.tc.host_dumper.dump_dir))
106
107 self.tearDownLocal()
108
109 # Method to be run after tearDown and implemented by child classes
110 def tearDownLocal(self):
111 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500112
113 #TODO: use package_manager.py to install packages on any type of image
114 def install_packages(self, packagelist):
115 for package in packagelist:
116 (status, result) = self.target.run("smart install -y "+package)
117 if status != 0:
118 return status
119
120class oeSDKTest(oeTest):
121 def __init__(self, methodName='runTest'):
122 self.sdktestdir = oeSDKTest.tc.sdktestdir
123 super(oeSDKTest, self).__init__(methodName)
124
125 @classmethod
126 def hasHostPackage(self, pkg):
127
128 if re.search(pkg, oeTest.tc.hostpkgmanifest):
129 return True
130 return False
131
132 def _run(self, cmd):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500133 return subprocess.check_output(". %s > /dev/null; %s;" % (self.tc.sdkenv, cmd), shell=True)
134
135class oeSDKExtTest(oeSDKTest):
136 def _run(self, cmd):
137 # extensible sdk shows a warning if found bitbake in the path
138 # because can cause contamination, i.e. use devtool from
139 # poky/scripts instead of eSDK one.
140 env = os.environ.copy()
141 paths_to_avoid = ['bitbake/bin', 'poky/scripts']
142 env['PATH'] = avoid_paths_in_environ(paths_to_avoid)
143
144 return subprocess.check_output(". %s > /dev/null;"\
145 " %s;" % (self.tc.sdkenv, cmd), shell=True, env=env)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500146
147def getmodule(pos=2):
148 # stack returns a list of tuples containg frame information
149 # First element of the list the is current frame, caller is 1
150 frameinfo = inspect.stack()[pos]
151 modname = inspect.getmodulename(frameinfo[1])
152 #modname = inspect.getmodule(frameinfo[0]).__name__
153 return modname
154
155def skipModule(reason, pos=2):
156 modname = getmodule(pos)
157 if modname not in oeTest.tc.testsrequired:
158 raise unittest.SkipTest("%s: %s" % (modname, reason))
159 else:
160 raise Exception("\nTest %s wants to be skipped.\nReason is: %s" \
161 "\nTest was required in TEST_SUITES, so either the condition for skipping is wrong" \
162 "\nor the image really doesn't have the required feature/package when it should." % (modname, reason))
163
164def skipModuleIf(cond, reason):
165
166 if cond:
167 skipModule(reason, 3)
168
169def skipModuleUnless(cond, reason):
170
171 if not cond:
172 skipModule(reason, 3)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500173
174_buffer_logger = ""
175def custom_verbose(msg, *args, **kwargs):
176 global _buffer_logger
177 if msg[-1] != "\n":
178 _buffer_logger += msg
179 else:
180 _buffer_logger += msg
181 try:
182 bb.plain(_buffer_logger.rstrip("\n"), *args, **kwargs)
183 except NameError:
184 logger.info(_buffer_logger.rstrip("\n"), *args, **kwargs)
185 _buffer_logger = ""
186
187class TestContext(object):
188 def __init__(self, d):
189 self.d = d
190
191 self.testsuites = self._get_test_suites()
192 self.testslist = self._get_tests_list(d.getVar("BBPATH", True).split(':'))
193 self.testsrequired = self._get_test_suites_required()
194
195 self.filesdir = os.path.join(os.path.dirname(os.path.abspath(
196 oeqa.runtime.__file__)), "files")
197 self.imagefeatures = d.getVar("IMAGE_FEATURES", True).split()
198 self.distrofeatures = d.getVar("DISTRO_FEATURES", True).split()
199
200 # get testcase list from specified file
201 # if path is a relative path, then relative to build/conf/
202 def _read_testlist(self, fpath, builddir):
203 if not os.path.isabs(fpath):
204 fpath = os.path.join(builddir, "conf", fpath)
205 if not os.path.exists(fpath):
206 bb.fatal("No such manifest file: ", fpath)
207 tcs = []
208 for line in open(fpath).readlines():
209 line = line.strip()
210 if line and not line.startswith("#"):
211 tcs.append(line)
212 return " ".join(tcs)
213
214 # return test list by type also filter if TEST_SUITES is specified
215 def _get_tests_list(self, bbpath):
216 testslist = []
217
218 type = self._get_test_namespace()
219
220 # This relies on lib/ under each directory in BBPATH being added to sys.path
221 # (as done by default in base.bbclass)
222 for testname in self.testsuites:
223 if testname != "auto":
224 if testname.startswith("oeqa."):
225 testslist.append(testname)
226 continue
227 found = False
228 for p in bbpath:
229 if os.path.exists(os.path.join(p, 'lib', 'oeqa', type, testname + '.py')):
230 testslist.append("oeqa." + type + "." + testname)
231 found = True
232 break
233 elif os.path.exists(os.path.join(p, 'lib', 'oeqa', type, testname.split(".")[0] + '.py')):
234 testslist.append("oeqa." + type + "." + testname)
235 found = True
236 break
237 if not found:
238 bb.fatal('Test %s specified in TEST_SUITES could not be found in lib/oeqa/runtime under BBPATH' % testname)
239
240 if "auto" in self.testsuites:
241 def add_auto_list(path):
242 if not os.path.exists(os.path.join(path, '__init__.py')):
243 bb.fatal('Tests directory %s exists but is missing __init__.py' % path)
244 files = sorted([f for f in os.listdir(path) if f.endswith('.py') and not f.startswith('_')])
245 for f in files:
246 module = 'oeqa.' + type + '.' + f[:-3]
247 if module not in testslist:
248 testslist.append(module)
249
250 for p in bbpath:
251 testpath = os.path.join(p, 'lib', 'oeqa', type)
252 bb.debug(2, 'Searching for tests in %s' % testpath)
253 if os.path.exists(testpath):
254 add_auto_list(testpath)
255
256 return testslist
257
258 def loadTests(self):
259 setattr(oeTest, "tc", self)
260
261 testloader = unittest.TestLoader()
262 testloader.sortTestMethodsUsing = None
263 suites = [testloader.loadTestsFromName(name) for name in self.testslist]
264 suites = filterByTagExp(suites, getattr(self, "tagexp", None))
265
266 def getTests(test):
267 '''Return all individual tests executed when running the suite.'''
268 # Unfortunately unittest does not have an API for this, so we have
269 # to rely on implementation details. This only needs to work
270 # for TestSuite containing TestCase.
271 method = getattr(test, '_testMethodName', None)
272 if method:
273 # leaf case: a TestCase
274 yield test
275 else:
276 # Look into TestSuite.
277 tests = getattr(test, '_tests', [])
278 for t1 in tests:
279 for t2 in getTests(t1):
280 yield t2
281
282 # Determine dependencies between suites by looking for @skipUnlessPassed
283 # method annotations. Suite A depends on suite B if any method in A
284 # depends on a method on B.
285 for suite in suites:
286 suite.dependencies = []
287 suite.depth = 0
288 for test in getTests(suite):
289 methodname = getattr(test, '_testMethodName', None)
290 if methodname:
291 method = getattr(test, methodname)
292 depends_on = getattr(method, '_depends_on', None)
293 if depends_on:
294 for dep_suite in suites:
295 if depends_on in [getattr(t, '_testMethodName', None) for t in getTests(dep_suite)]:
296 if dep_suite not in suite.dependencies and \
297 dep_suite is not suite:
298 suite.dependencies.append(dep_suite)
299 break
300 else:
301 logger.warning("Test %s was declared as @skipUnlessPassed('%s') but that test is either not defined or not active. Will run the test anyway." %
302 (test, depends_on))
303
304 # Use brute-force topological sort to determine ordering. Sort by
305 # depth (higher depth = must run later), with original ordering to
306 # break ties.
307 def set_suite_depth(suite):
308 for dep in suite.dependencies:
309 new_depth = set_suite_depth(dep) + 1
310 if new_depth > suite.depth:
311 suite.depth = new_depth
312 return suite.depth
313
314 for index, suite in enumerate(suites):
315 set_suite_depth(suite)
316 suite.index = index
317 suites.sort(cmp=lambda a,b: cmp((a.depth, a.index), (b.depth, b.index)))
318
319 self.suite = testloader.suiteClass(suites)
320
321 return self.suite
322
323 def runTests(self):
324 logger.info("Test modules %s" % self.testslist)
325 if hasattr(self, "tagexp") and self.tagexp:
326 logger.info("Filter test cases by tags: %s" % self.tagexp)
327 logger.info("Found %s tests" % self.suite.countTestCases())
328 runner = unittest.TextTestRunner(verbosity=2)
329 if 'bb' in sys.modules:
330 runner.stream.write = custom_verbose
331
332 return runner.run(self.suite)
333
334class ImageTestContext(TestContext):
335 def __init__(self, d, target, host_dumper):
336 super(ImageTestContext, self).__init__(d)
337
338 self.tagexp = d.getVar("TEST_SUITES_TAGS", True)
339
340 self.target = target
341 self.host_dumper = host_dumper
342
343 manifest = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True),
344 d.getVar("IMAGE_LINK_NAME", True) + ".manifest")
345 nomanifest = d.getVar("IMAGE_NO_MANIFEST", True)
346 if nomanifest is None or nomanifest != "1":
347 try:
348 with open(manifest) as f:
349 self.pkgmanifest = f.read()
350 except IOError as e:
351 bb.fatal("No package manifest file found. Did you build the image?\n%s" % e)
352 else:
353 self.pkgmanifest = ""
354
355 self.sigterm = False
356 self.origsigtermhandler = signal.getsignal(signal.SIGTERM)
357 signal.signal(signal.SIGTERM, self._sigterm_exception)
358
359 def _sigterm_exception(self, signum, stackframe):
360 bb.warn("TestImage received SIGTERM, shutting down...")
361 self.sigterm = True
362 self.target.stop()
363
364 def _get_test_namespace(self):
365 return "runtime"
366
367 def _get_test_suites(self):
368 testsuites = []
369
370 manifests = (self.d.getVar("TEST_SUITES_MANIFEST", True) or '').split()
371 if manifests:
372 for manifest in manifests:
373 testsuites.extend(self._read_testlist(manifest,
374 self.d.getVar("TOPDIR", True)).split())
375
376 else:
377 testsuites = self.d.getVar("TEST_SUITES", True).split()
378
379 return testsuites
380
381 def _get_test_suites_required(self):
382 return [t for t in self.d.getVar("TEST_SUITES", True).split() if t != "auto"]
383
384 def loadTests(self):
385 super(ImageTestContext, self).loadTests()
386 setattr(oeRuntimeTest, "pscmd", "ps -ef" if oeTest.hasPackage("procps") else "ps")
387
388class SDKTestContext(TestContext):
389 def __init__(self, d, sdktestdir, sdkenv, tcname, *args):
390 super(SDKTestContext, self).__init__(d)
391
392 self.sdktestdir = sdktestdir
393 self.sdkenv = sdkenv
394 self.tcname = tcname
395
396 if not hasattr(self, 'target_manifest'):
397 self.target_manifest = d.getVar("SDK_TARGET_MANIFEST", True)
398 try:
399 with open(self.target_manifest) as f:
400 self.pkgmanifest = f.read()
401 except IOError as e:
402 bb.fatal("No package manifest file found. Did you build the sdk image?\n%s" % e)
403
404 if not hasattr(self, 'host_manifest'):
405 self.host_manifest = d.getVar("SDK_HOST_MANIFEST", True)
406 try:
407 with open(self.host_manifest) as f:
408 self.hostpkgmanifest = f.read()
409 except IOError as e:
410 bb.fatal("No host package manifest file found. Did you build the sdk image?\n%s" % e)
411
412 def _get_test_namespace(self):
413 return "sdk"
414
415 def _get_test_suites(self):
416 return (self.d.getVar("TEST_SUITES_SDK", True) or "auto").split()
417
418 def _get_test_suites_required(self):
419 return [t for t in (self.d.getVar("TEST_SUITES_SDK", True) or \
420 "auto").split() if t != "auto"]
421
422class SDKExtTestContext(SDKTestContext):
423 def __init__(self, d, sdktestdir, sdkenv, tcname, *args):
424 self.target_manifest = d.getVar("SDK_EXT_TARGET_MANIFEST", True)
425 self.host_manifest = d.getVar("SDK_EXT_HOST_MANIFEST", True)
426 if args:
427 self.cm = args[0] # Compatibility mode for run SDK tests
428 else:
429 self.cm = False
430
431 super(SDKExtTestContext, self).__init__(d, sdktestdir, sdkenv, tcname)
432
433 self.sdkextfilesdir = os.path.join(os.path.dirname(os.path.abspath(
434 oeqa.sdkext.__file__)), "files")
435
436 def _get_test_namespace(self):
437 if self.cm:
438 return "sdk"
439 else:
440 return "sdkext"
441
442 def _get_test_suites(self):
443 return (self.d.getVar("TEST_SUITES_SDK_EXT", True) or "auto").split()
444
445 def _get_test_suites_required(self):
446 return [t for t in (self.d.getVar("TEST_SUITES_SDK_EXT", True) or \
447 "auto").split() if t != "auto"]