summaryrefslogtreecommitdiffstats
path: root/meta/lib/oeqa/selftest/context.py
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib/oeqa/selftest/context.py')
-rw-r--r--meta/lib/oeqa/selftest/context.py148
1 files changed, 77 insertions, 71 deletions
diff --git a/meta/lib/oeqa/selftest/context.py b/meta/lib/oeqa/selftest/context.py
index 1659926975..99186175e5 100644
--- a/meta/lib/oeqa/selftest/context.py
+++ b/meta/lib/oeqa/selftest/context.py
@@ -16,19 +16,32 @@ from random import choice
16import oeqa 16import oeqa
17import oe 17import oe
18import bb.utils 18import bb.utils
19import bb.tinfoil
19 20
20from oeqa.core.context import OETestContext, OETestContextExecutor 21from oeqa.core.context import OETestContext, OETestContextExecutor
21from oeqa.core.exception import OEQAPreRun, OEQATestNotFound 22from oeqa.core.exception import OEQAPreRun, OEQATestNotFound
22 23
23from oeqa.utils.commands import runCmd, get_bb_vars, get_test_layer 24from oeqa.utils.commands import runCmd, get_bb_vars, get_test_layer
24 25
26OESELFTEST_METADATA=["run_all_tests", "run_tests", "skips", "machine", "select_tags", "exclude_tags"]
27
28def get_oeselftest_metadata(args):
29 result = {}
30 raw_args = vars(args)
31 for metadata in OESELFTEST_METADATA:
32 if metadata in raw_args:
33 result[metadata] = raw_args[metadata]
34
35 return result
36
25class NonConcurrentTestSuite(unittest.TestSuite): 37class NonConcurrentTestSuite(unittest.TestSuite):
26 def __init__(self, suite, processes, setupfunc, removefunc): 38 def __init__(self, suite, processes, setupfunc, removefunc, bb_vars):
27 super().__init__([suite]) 39 super().__init__([suite])
28 self.processes = processes 40 self.processes = processes
29 self.suite = suite 41 self.suite = suite
30 self.setupfunc = setupfunc 42 self.setupfunc = setupfunc
31 self.removefunc = removefunc 43 self.removefunc = removefunc
44 self.bb_vars = bb_vars
32 45
33 def run(self, result): 46 def run(self, result):
34 (builddir, newbuilddir) = self.setupfunc("-st", None, self.suite) 47 (builddir, newbuilddir) = self.setupfunc("-st", None, self.suite)
@@ -39,7 +52,7 @@ class NonConcurrentTestSuite(unittest.TestSuite):
39 52
40def removebuilddir(d): 53def removebuilddir(d):
41 delay = 5 54 delay = 5
42 while delay and os.path.exists(d + "/bitbake.lock"): 55 while delay and (os.path.exists(d + "/bitbake.lock") or os.path.exists(d + "/cache/hashserv.db-wal")):
43 time.sleep(1) 56 time.sleep(1)
44 delay = delay - 1 57 delay = delay - 1
45 # Deleting these directories takes a lot of time, use autobuilder 58 # Deleting these directories takes a lot of time, use autobuilder
@@ -57,8 +70,6 @@ class OESelftestTestContext(OETestContext):
57 def __init__(self, td=None, logger=None, machines=None, config_paths=None, newbuilddir=None, keep_builddir=None): 70 def __init__(self, td=None, logger=None, machines=None, config_paths=None, newbuilddir=None, keep_builddir=None):
58 super(OESelftestTestContext, self).__init__(td, logger) 71 super(OESelftestTestContext, self).__init__(td, logger)
59 72
60 self.machines = machines
61 self.custommachine = None
62 self.config_paths = config_paths 73 self.config_paths = config_paths
63 self.newbuilddir = newbuilddir 74 self.newbuilddir = newbuilddir
64 75
@@ -67,10 +78,15 @@ class OESelftestTestContext(OETestContext):
67 else: 78 else:
68 self.removebuilddir = removebuilddir 79 self.removebuilddir = removebuilddir
69 80
81 def set_variables(self, vars):
82 self.bb_vars = vars
83
70 def setup_builddir(self, suffix, selftestdir, suite): 84 def setup_builddir(self, suffix, selftestdir, suite):
85 sstatedir = self.bb_vars['SSTATE_DIR']
86
71 builddir = os.environ['BUILDDIR'] 87 builddir = os.environ['BUILDDIR']
72 if not selftestdir: 88 if not selftestdir:
73 selftestdir = get_test_layer() 89 selftestdir = get_test_layer(self.bb_vars['BBLAYERS'])
74 if self.newbuilddir: 90 if self.newbuilddir:
75 newbuilddir = os.path.join(self.newbuilddir, 'build' + suffix) 91 newbuilddir = os.path.join(self.newbuilddir, 'build' + suffix)
76 else: 92 else:
@@ -86,16 +102,29 @@ class OESelftestTestContext(OETestContext):
86 oe.path.copytree(builddir + "/cache", newbuilddir + "/cache") 102 oe.path.copytree(builddir + "/cache", newbuilddir + "/cache")
87 oe.path.copytree(selftestdir, newselftestdir) 103 oe.path.copytree(selftestdir, newselftestdir)
88 104
105 subprocess.check_output("git init && git add * && git commit -a -m 'initial'", cwd=newselftestdir, shell=True)
106
107 # Tried to used bitbake-layers add/remove but it requires recipe parsing and hence is too slow
108 subprocess.check_output("sed %s/conf/bblayers.conf -i -e 's#%s#%s#g'" % (newbuilddir, selftestdir, newselftestdir), cwd=newbuilddir, shell=True)
109
110 # Relative paths in BBLAYERS only works when the new build dir share the same ascending node
111 if self.newbuilddir:
112 bblayers = subprocess.check_output("bitbake-getvar --value BBLAYERS | tail -1", cwd=builddir, shell=True, text=True)
113 if '..' in bblayers:
114 bblayers_abspath = [os.path.abspath(path) for path in bblayers.split()]
115 with open("%s/conf/bblayers.conf" % newbuilddir, "a") as f:
116 newbblayers = "# new bblayers to be used by selftest in the new build dir '%s'\n" % newbuilddir
117 newbblayers += 'BBLAYERS = "%s"\n' % ' '.join(bblayers_abspath)
118 f.write(newbblayers)
119
89 for e in os.environ: 120 for e in os.environ:
90 if builddir + "/" in os.environ[e]: 121 if builddir + "/" in os.environ[e]:
91 os.environ[e] = os.environ[e].replace(builddir + "/", newbuilddir + "/") 122 os.environ[e] = os.environ[e].replace(builddir + "/", newbuilddir + "/")
92 if os.environ[e].endswith(builddir): 123 if os.environ[e].endswith(builddir):
93 os.environ[e] = os.environ[e].replace(builddir, newbuilddir) 124 os.environ[e] = os.environ[e].replace(builddir, newbuilddir)
94 125
95 subprocess.check_output("git init; git add *; git commit -a -m 'initial'", cwd=newselftestdir, shell=True) 126 # Set SSTATE_DIR to match the parent SSTATE_DIR
96 127 subprocess.check_output("echo 'SSTATE_DIR ?= \"%s\"' >> %s/conf/local.conf" % (sstatedir, newbuilddir), cwd=newbuilddir, shell=True)
97 # Tried to used bitbake-layers add/remove but it requires recipe parsing and hence is too slow
98 subprocess.check_output("sed %s/conf/bblayers.conf -i -e 's#%s#%s#g'" % (newbuilddir, selftestdir, newselftestdir), cwd=newbuilddir, shell=True)
99 128
100 os.chdir(newbuilddir) 129 os.chdir(newbuilddir)
101 130
@@ -124,17 +153,11 @@ class OESelftestTestContext(OETestContext):
124 if processes: 153 if processes:
125 from oeqa.core.utils.concurrencytest import ConcurrentTestSuite 154 from oeqa.core.utils.concurrencytest import ConcurrentTestSuite
126 155
127 return ConcurrentTestSuite(suites, processes, self.setup_builddir, self.removebuilddir) 156 return ConcurrentTestSuite(suites, processes, self.setup_builddir, self.removebuilddir, self.bb_vars)
128 else: 157 else:
129 return NonConcurrentTestSuite(suites, processes, self.setup_builddir, self.removebuilddir) 158 return NonConcurrentTestSuite(suites, processes, self.setup_builddir, self.removebuilddir, self.bb_vars)
130 159
131 def runTests(self, processes=None, machine=None, skips=[]): 160 def runTests(self, processes=None, machine=None, skips=[]):
132 if machine:
133 self.custommachine = machine
134 if machine == 'random':
135 self.custommachine = choice(self.machines)
136 self.logger.info('Run tests with custom MACHINE set to: %s' % \
137 self.custommachine)
138 return super(OESelftestTestContext, self).runTests(processes, skips) 161 return super(OESelftestTestContext, self).runTests(processes, skips)
139 162
140 def listTests(self, display_type, machine=None): 163 def listTests(self, display_type, machine=None):
@@ -154,9 +177,6 @@ class OESelftestTestContextExecutor(OETestContextExecutor):
154 group.add_argument('-a', '--run-all-tests', default=False, 177 group.add_argument('-a', '--run-all-tests', default=False,
155 action="store_true", dest="run_all_tests", 178 action="store_true", dest="run_all_tests",
156 help='Run all (unhidden) tests') 179 help='Run all (unhidden) tests')
157 group.add_argument('-R', '--skip-tests', required=False, action='store',
158 nargs='+', dest="skips", default=None,
159 help='Run all (unhidden) tests except the ones specified. Format should be <module>[.<class>[.<test_method>]]')
160 group.add_argument('-r', '--run-tests', required=False, action='store', 180 group.add_argument('-r', '--run-tests', required=False, action='store',
161 nargs='+', dest="run_tests", default=None, 181 nargs='+', dest="run_tests", default=None,
162 help='Select what tests to run (modules, classes or test methods). Format should be: <module>.<class>.<test_method>') 182 help='Select what tests to run (modules, classes or test methods). Format should be: <module>.<class>.<test_method>')
@@ -171,11 +191,26 @@ class OESelftestTestContextExecutor(OETestContextExecutor):
171 action="store_true", default=False, 191 action="store_true", default=False,
172 help='List all available tests.') 192 help='List all available tests.')
173 193
174 parser.add_argument('-j', '--num-processes', dest='processes', action='store', 194 parser.add_argument('-R', '--skip-tests', required=False, action='store',
175 type=int, help="number of processes to execute in parallel with") 195 nargs='+', dest="skips", default=None,
196 help='Skip the tests specified. Format should be <module>[.<class>[.<test_method>]]')
197
198 def check_parallel_support(parameter):
199 if not parameter.isdigit():
200 import argparse
201 raise argparse.ArgumentTypeError("argument -j/--num-processes: invalid int value: '%s' " % str(parameter))
202
203 processes = int(parameter)
204 if processes:
205 try:
206 import testtools, subunit
207 except ImportError:
208 print("Failed to import testtools or subunit, the testcases will run serially")
209 processes = None
210 return processes
176 211
177 parser.add_argument('--machine', required=False, choices=['random', 'all'], 212 parser.add_argument('-j', '--num-processes', dest='processes', action='store',
178 help='Run tests on different machines (random/all).') 213 type=check_parallel_support, help="number of processes to execute in parallel with")
179 214
180 parser.add_argument('-t', '--select-tag', dest="select_tags", 215 parser.add_argument('-t', '--select-tag', dest="select_tags",
181 action='append', default=None, 216 action='append', default=None,
@@ -191,20 +226,6 @@ class OESelftestTestContextExecutor(OETestContextExecutor):
191 parser.add_argument('-v', '--verbose', action='store_true') 226 parser.add_argument('-v', '--verbose', action='store_true')
192 parser.set_defaults(func=self.run) 227 parser.set_defaults(func=self.run)
193 228
194 def _get_available_machines(self):
195 machines = []
196
197 bbpath = self.tc_kwargs['init']['td']['BBPATH'].split(':')
198
199 for path in bbpath:
200 found_machines = glob.glob(os.path.join(path, 'conf', 'machine', '*.conf'))
201 if found_machines:
202 for i in found_machines:
203 # eg: '/home/<user>/poky/meta-intel/conf/machine/intel-core2-32.conf'
204 machines.append(os.path.splitext(os.path.basename(i))[0])
205
206 return machines
207
208 def _get_cases_paths(self, bbpath): 229 def _get_cases_paths(self, bbpath):
209 cases_paths = [] 230 cases_paths = []
210 for layer in bbpath: 231 for layer in bbpath:
@@ -235,11 +256,10 @@ class OESelftestTestContextExecutor(OETestContextExecutor):
235 args.list_tests = 'name' 256 args.list_tests = 'name'
236 257
237 self.tc_kwargs['init']['td'] = bbvars 258 self.tc_kwargs['init']['td'] = bbvars
238 self.tc_kwargs['init']['machines'] = self._get_available_machines()
239 259
240 builddir = os.environ.get("BUILDDIR") 260 builddir = os.environ.get("BUILDDIR")
241 self.tc_kwargs['init']['config_paths'] = {} 261 self.tc_kwargs['init']['config_paths'] = {}
242 self.tc_kwargs['init']['config_paths']['testlayer_path'] = get_test_layer() 262 self.tc_kwargs['init']['config_paths']['testlayer_path'] = get_test_layer(bbvars["BBLAYERS"])
243 self.tc_kwargs['init']['config_paths']['builddir'] = builddir 263 self.tc_kwargs['init']['config_paths']['builddir'] = builddir
244 self.tc_kwargs['init']['config_paths']['localconf'] = os.path.join(builddir, "conf/local.conf") 264 self.tc_kwargs['init']['config_paths']['localconf'] = os.path.join(builddir, "conf/local.conf")
245 self.tc_kwargs['init']['config_paths']['bblayers'] = os.path.join(builddir, "conf/bblayers.conf") 265 self.tc_kwargs['init']['config_paths']['bblayers'] = os.path.join(builddir, "conf/bblayers.conf")
@@ -275,14 +295,14 @@ class OESelftestTestContextExecutor(OETestContextExecutor):
275 os.chdir(builddir) 295 os.chdir(builddir)
276 296
277 if not "meta-selftest" in self.tc.td["BBLAYERS"]: 297 if not "meta-selftest" in self.tc.td["BBLAYERS"]:
278 self.tc.logger.warning("meta-selftest layer not found in BBLAYERS, adding it") 298 self.tc.logger.info("meta-selftest layer not found in BBLAYERS, adding it")
279 meta_selftestdir = os.path.join( 299 meta_selftestdir = os.path.join(
280 self.tc.td["BBLAYERS_FETCH_DIR"], 'meta-selftest') 300 self.tc.td["BBLAYERS_FETCH_DIR"], 'meta-selftest')
281 if os.path.isdir(meta_selftestdir): 301 if os.path.isdir(meta_selftestdir):
282 runCmd("bitbake-layers add-layer %s" %meta_selftestdir) 302 runCmd("bitbake-layers add-layer %s" % meta_selftestdir)
283 # reload data is needed because a meta-selftest layer was add 303 # reload data is needed because a meta-selftest layer was add
284 self.tc.td = get_bb_vars() 304 self.tc.td = get_bb_vars()
285 self.tc.config_paths['testlayer_path'] = get_test_layer() 305 self.tc.config_paths['testlayer_path'] = get_test_layer(self.tc.td["BBLAYERS"])
286 else: 306 else:
287 self.tc.logger.error("could not locate meta-selftest in:\n%s" % meta_selftestdir) 307 self.tc.logger.error("could not locate meta-selftest in:\n%s" % meta_selftestdir)
288 raise OEQAPreRun 308 raise OEQAPreRun
@@ -320,8 +340,15 @@ class OESelftestTestContextExecutor(OETestContextExecutor):
320 340
321 _add_layer_libs() 341 _add_layer_libs()
322 342
323 self.tc.logger.info("Running bitbake -e to test the configuration is valid/parsable") 343 self.tc.logger.info("Checking base configuration is valid/parsable")
324 runCmd("bitbake -e") 344
345 with bb.tinfoil.Tinfoil(tracking=True) as tinfoil:
346 tinfoil.prepare(quiet=2, config_only=True)
347 d = tinfoil.config_data
348 vars = {}
349 vars['SSTATE_DIR'] = str(d.getVar('SSTATE_DIR'))
350 vars['BBLAYERS'] = str(d.getVar('BBLAYERS'))
351 self.tc.set_variables(vars)
325 352
326 def get_json_result_dir(self, args): 353 def get_json_result_dir(self, args):
327 json_result_dir = os.path.join(self.tc.td["LOG_DIR"], 'oeqa') 354 json_result_dir = os.path.join(self.tc.td["LOG_DIR"], 'oeqa')
@@ -334,12 +361,14 @@ class OESelftestTestContextExecutor(OETestContextExecutor):
334 import platform 361 import platform
335 from oeqa.utils.metadata import metadata_from_bb 362 from oeqa.utils.metadata import metadata_from_bb
336 metadata = metadata_from_bb() 363 metadata = metadata_from_bb()
364 oeselftest_metadata = get_oeselftest_metadata(args)
337 configuration = {'TEST_TYPE': 'oeselftest', 365 configuration = {'TEST_TYPE': 'oeselftest',
338 'STARTTIME': args.test_start_time, 366 'STARTTIME': args.test_start_time,
339 'MACHINE': self.tc.td["MACHINE"], 367 'MACHINE': self.tc.td["MACHINE"],
340 'HOST_DISTRO': oe.lsb.distro_identifier().replace(' ', '-'), 368 'HOST_DISTRO': oe.lsb.distro_identifier().replace(' ', '-'),
341 'HOST_NAME': metadata['hostname'], 369 'HOST_NAME': metadata['hostname'],
342 'LAYERS': metadata['layers']} 370 'LAYERS': metadata['layers'],
371 'OESELFTEST_METADATA': oeselftest_metadata}
343 return configuration 372 return configuration
344 373
345 def get_result_id(self, configuration): 374 def get_result_id(self, configuration):
@@ -374,37 +403,14 @@ class OESelftestTestContextExecutor(OETestContextExecutor):
374 403
375 rc = None 404 rc = None
376 try: 405 try:
377 if args.machine: 406 rc = self._internal_run(logger, args)
378 logger.info('Custom machine mode enabled. MACHINE set to %s' %
379 args.machine)
380
381 if args.machine == 'all':
382 results = []
383 for m in self.tc_kwargs['init']['machines']:
384 self.tc_kwargs['run']['machine'] = m
385 results.append(self._internal_run(logger, args))
386
387 # XXX: the oe-selftest script only needs to know if one
388 # machine run fails
389 for r in results:
390 rc = r
391 if not r.wasSuccessful():
392 break
393
394 else:
395 self.tc_kwargs['run']['machine'] = args.machine
396 return self._internal_run(logger, args)
397
398 else:
399 self.tc_kwargs['run']['machine'] = args.machine
400 rc = self._internal_run(logger, args)
401 finally: 407 finally:
402 config_paths = self.tc_kwargs['init']['config_paths'] 408 config_paths = self.tc_kwargs['init']['config_paths']
403 409
404 output_link = os.path.join(os.path.dirname(args.output_log), 410 output_link = os.path.join(os.path.dirname(args.output_log),
405 "%s-results.log" % self.name) 411 "%s-results.log" % self.name)
406 if os.path.lexists(output_link): 412 if os.path.lexists(output_link):
407 os.remove(output_link) 413 os.unlink(output_link)
408 os.symlink(args.output_log, output_link) 414 os.symlink(args.output_log, output_link)
409 415
410 return rc 416 return rc