blob: 0a7a9da72afe333bf8d5fc911e471c06a14d58e6 [file] [log] [blame]
Brad Bishopc342db32019-05-15 21:57:59 -04001#
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002# Copyright (C) 2017 Intel Corporation
Brad Bishopc342db32019-05-15 21:57:59 -04003#
4# SPDX-License-Identifier: MIT
5#
Brad Bishopd7bf8c12018-02-25 22:55:05 -05006
7import os
8import time
9import glob
10import sys
Brad Bishopf86d0552018-12-04 14:18:15 -080011import importlib
Andrew Geissler82c905d2020-04-13 13:39:40 -050012import subprocess
Andrew Geissler4ed12e12020-06-05 18:00:41 -050013import unittest
Brad Bishopd7bf8c12018-02-25 22:55:05 -050014from random import choice
15
16import oeqa
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080017import oe
Andrew Geissler82c905d2020-04-13 13:39:40 -050018import bb.utils
Brad Bishopd7bf8c12018-02-25 22:55:05 -050019
20from oeqa.core.context import OETestContext, OETestContextExecutor
21from oeqa.core.exception import OEQAPreRun, OEQATestNotFound
22
23from oeqa.utils.commands import runCmd, get_bb_vars, get_test_layer
24
Andrew Geissler6aa7eec2023-03-03 12:41:14 -060025OESELFTEST_METADATA=["run_all_tests", "run_tests", "skips", "machine", "select_tags", "exclude_tags"]
26
27def get_oeselftest_metadata(args):
28 result = {}
29 raw_args = vars(args)
30 for metadata in OESELFTEST_METADATA:
31 if metadata in raw_args:
32 result[metadata] = raw_args[metadata]
33
34 return result
35
Andrew Geissler475cb722020-07-10 16:00:51 -050036class NonConcurrentTestSuite(unittest.TestSuite):
37 def __init__(self, suite, processes, setupfunc, removefunc):
38 super().__init__([suite])
39 self.processes = processes
40 self.suite = suite
41 self.setupfunc = setupfunc
42 self.removefunc = removefunc
43
44 def run(self, result):
45 (builddir, newbuilddir) = self.setupfunc("-st", None, self.suite)
46 ret = super().run(result)
47 os.chdir(builddir)
Andrew Geisslerd1e89492021-02-12 15:35:20 -060048 if newbuilddir and ret.wasSuccessful() and self.removefunc:
Andrew Geissler475cb722020-07-10 16:00:51 -050049 self.removefunc(newbuilddir)
50
51def removebuilddir(d):
52 delay = 5
Andrew Geisslereff27472021-10-29 15:35:00 -050053 while delay and (os.path.exists(d + "/bitbake.lock") or os.path.exists(d + "/cache/hashserv.db-wal")):
Andrew Geissler475cb722020-07-10 16:00:51 -050054 time.sleep(1)
55 delay = delay - 1
56 # Deleting these directories takes a lot of time, use autobuilder
57 # clobberdir if its available
58 clobberdir = os.path.expanduser("~/yocto-autobuilder-helper/janitor/clobberdir")
59 if os.path.exists(clobberdir):
60 try:
61 subprocess.check_call([clobberdir, d])
62 return
63 except subprocess.CalledProcessError:
64 pass
65 bb.utils.prunedir(d, ionice=True)
66
Brad Bishopd7bf8c12018-02-25 22:55:05 -050067class OESelftestTestContext(OETestContext):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060068 def __init__(self, td=None, logger=None, machines=None, config_paths=None, newbuilddir=None, keep_builddir=None):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050069 super(OESelftestTestContext, self).__init__(td, logger)
70
71 self.machines = machines
72 self.custommachine = None
73 self.config_paths = config_paths
Andrew Geissler4ed12e12020-06-05 18:00:41 -050074 self.newbuilddir = newbuilddir
Brad Bishopd7bf8c12018-02-25 22:55:05 -050075
Andrew Geisslerd1e89492021-02-12 15:35:20 -060076 if keep_builddir:
77 self.removebuilddir = None
78 else:
79 self.removebuilddir = removebuilddir
80
Andrew Geissler82c905d2020-04-13 13:39:40 -050081 def setup_builddir(self, suffix, selftestdir, suite):
82 builddir = os.environ['BUILDDIR']
83 if not selftestdir:
84 selftestdir = get_test_layer()
Andrew Geissler4ed12e12020-06-05 18:00:41 -050085 if self.newbuilddir:
86 newbuilddir = os.path.join(self.newbuilddir, 'build' + suffix)
87 else:
88 newbuilddir = builddir + suffix
Andrew Geissler82c905d2020-04-13 13:39:40 -050089 newselftestdir = newbuilddir + "/meta-selftest"
90
91 if os.path.exists(newbuilddir):
92 self.logger.error("Build directory %s already exists, aborting" % newbuilddir)
93 sys.exit(1)
94
95 bb.utils.mkdirhier(newbuilddir)
96 oe.path.copytree(builddir + "/conf", newbuilddir + "/conf")
97 oe.path.copytree(builddir + "/cache", newbuilddir + "/cache")
98 oe.path.copytree(selftestdir, newselftestdir)
99
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500100 subprocess.check_output("git init; git add *; git commit -a -m 'initial'", cwd=newselftestdir, shell=True)
101
102 # Tried to used bitbake-layers add/remove but it requires recipe parsing and hence is too slow
103 subprocess.check_output("sed %s/conf/bblayers.conf -i -e 's#%s#%s#g'" % (newbuilddir, selftestdir, newselftestdir), cwd=newbuilddir, shell=True)
104
105 # Relative paths in BBLAYERS only works when the new build dir share the same ascending node
106 if self.newbuilddir:
107 bblayers = subprocess.check_output("bitbake-getvar --value BBLAYERS | tail -1", cwd=builddir, shell=True, text=True)
108 if '..' in bblayers:
109 bblayers_abspath = [os.path.abspath(path) for path in bblayers.split()]
110 with open("%s/conf/bblayers.conf" % newbuilddir, "a") as f:
111 newbblayers = "# new bblayers to be used by selftest in the new build dir '%s'\n" % newbuilddir
112 newbblayers += 'BBLAYERS = "%s"\n' % ' '.join(bblayers_abspath)
113 f.write(newbblayers)
114
Andrew Geissler82c905d2020-04-13 13:39:40 -0500115 for e in os.environ:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500116 if builddir + "/" in os.environ[e]:
117 os.environ[e] = os.environ[e].replace(builddir + "/", newbuilddir + "/")
118 if os.environ[e].endswith(builddir):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500119 os.environ[e] = os.environ[e].replace(builddir, newbuilddir)
120
Andrew Geissler82c905d2020-04-13 13:39:40 -0500121 os.chdir(newbuilddir)
122
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500123 def patch_test(t):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500124 if not hasattr(t, "tc"):
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500125 return
Andrew Geissler82c905d2020-04-13 13:39:40 -0500126 cp = t.tc.config_paths
127 for p in cp:
128 if selftestdir in cp[p] and newselftestdir not in cp[p]:
129 cp[p] = cp[p].replace(selftestdir, newselftestdir)
130 if builddir in cp[p] and newbuilddir not in cp[p]:
131 cp[p] = cp[p].replace(builddir, newbuilddir)
132
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500133 def patch_suite(s):
134 for x in s:
135 if isinstance(x, unittest.TestSuite):
136 patch_suite(x)
137 else:
138 patch_test(x)
139
140 patch_suite(suite)
141
Andrew Geissler82c905d2020-04-13 13:39:40 -0500142 return (builddir, newbuilddir)
143
144 def prepareSuite(self, suites, processes):
145 if processes:
146 from oeqa.core.utils.concurrencytest import ConcurrentTestSuite
147
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600148 return ConcurrentTestSuite(suites, processes, self.setup_builddir, self.removebuilddir)
Andrew Geissler82c905d2020-04-13 13:39:40 -0500149 else:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600150 return NonConcurrentTestSuite(suites, processes, self.setup_builddir, self.removebuilddir)
Andrew Geissler82c905d2020-04-13 13:39:40 -0500151
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800152 def runTests(self, processes=None, machine=None, skips=[]):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500153 if machine:
154 self.custommachine = machine
155 if machine == 'random':
156 self.custommachine = choice(self.machines)
157 self.logger.info('Run tests with custom MACHINE set to: %s' % \
158 self.custommachine)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800159 return super(OESelftestTestContext, self).runTests(processes, skips)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500160
161 def listTests(self, display_type, machine=None):
162 return super(OESelftestTestContext, self).listTests(display_type)
163
164class OESelftestTestContextExecutor(OETestContextExecutor):
165 _context_class = OESelftestTestContext
166 _script_executor = 'oe-selftest'
167
168 name = 'oe-selftest'
169 help = 'oe-selftest test component'
170 description = 'Executes selftest tests'
171
172 def register_commands(self, logger, parser):
173 group = parser.add_mutually_exclusive_group(required=True)
174
175 group.add_argument('-a', '--run-all-tests', default=False,
176 action="store_true", dest="run_all_tests",
177 help='Run all (unhidden) tests')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500178 group.add_argument('-r', '--run-tests', required=False, action='store',
179 nargs='+', dest="run_tests", default=None,
180 help='Select what tests to run (modules, classes or test methods). Format should be: <module>.<class>.<test_method>')
181
182 group.add_argument('-m', '--list-modules', required=False,
183 action="store_true", default=False,
184 help='List all available test modules.')
185 group.add_argument('--list-classes', required=False,
186 action="store_true", default=False,
187 help='List all available test classes.')
188 group.add_argument('-l', '--list-tests', required=False,
189 action="store_true", default=False,
190 help='List all available tests.')
191
Andrew Geissler517393d2023-01-13 08:55:19 -0600192 parser.add_argument('-R', '--skip-tests', required=False, action='store',
193 nargs='+', dest="skips", default=None,
194 help='Skip the tests specified. Format should be <module>[.<class>[.<test_method>]]')
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800195 parser.add_argument('-j', '--num-processes', dest='processes', action='store',
196 type=int, help="number of processes to execute in parallel with")
197
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500198 parser.add_argument('--machine', required=False, choices=['random', 'all'],
199 help='Run tests on different machines (random/all).')
Brad Bishop79641f22019-09-10 07:20:22 -0400200
Brad Bishopacc069e2019-09-13 06:48:36 -0400201 parser.add_argument('-t', '--select-tag', dest="select_tags",
202 action='append', default=None,
203 help='Filter all (unhidden) tests to any that match any of the specified tag(s).')
204 parser.add_argument('-T', '--exclude-tag', dest="exclude_tags",
205 action='append', default=None,
206 help='Exclude all (unhidden) tests that match any of the specified tag(s). (exclude applies before select)')
Brad Bishop79641f22019-09-10 07:20:22 -0400207
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600208 parser.add_argument('-K', '--keep-builddir', action='store_true',
209 help='Keep the test build directory even if all tests pass')
210
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500211 parser.add_argument('-B', '--newbuilddir', help='New build directory to use for tests.')
212 parser.add_argument('-v', '--verbose', action='store_true')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500213 parser.set_defaults(func=self.run)
214
215 def _get_available_machines(self):
216 machines = []
217
218 bbpath = self.tc_kwargs['init']['td']['BBPATH'].split(':')
219
220 for path in bbpath:
221 found_machines = glob.glob(os.path.join(path, 'conf', 'machine', '*.conf'))
222 if found_machines:
223 for i in found_machines:
224 # eg: '/home/<user>/poky/meta-intel/conf/machine/intel-core2-32.conf'
225 machines.append(os.path.splitext(os.path.basename(i))[0])
226
227 return machines
228
229 def _get_cases_paths(self, bbpath):
230 cases_paths = []
231 for layer in bbpath:
232 cases_dir = os.path.join(layer, 'lib', 'oeqa', 'selftest', 'cases')
233 if os.path.isdir(cases_dir):
234 cases_paths.append(cases_dir)
235 return cases_paths
236
237 def _process_args(self, logger, args):
Brad Bishopf86d0552018-12-04 14:18:15 -0800238 args.test_start_time = time.strftime("%Y%m%d%H%M%S")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500239 args.test_data_file = None
240 args.CASES_PATHS = None
241
Brad Bishopf86d0552018-12-04 14:18:15 -0800242 bbvars = get_bb_vars()
243 logdir = os.environ.get("BUILDDIR")
244 if 'LOG_DIR' in bbvars:
245 logdir = bbvars['LOG_DIR']
Brad Bishop19323692019-04-05 15:28:33 -0400246 bb.utils.mkdirhier(logdir)
Brad Bishopf86d0552018-12-04 14:18:15 -0800247 args.output_log = logdir + '/%s-results-%s.log' % (self.name, args.test_start_time)
248
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500249 super(OESelftestTestContextExecutor, self)._process_args(logger, args)
250
251 if args.list_modules:
252 args.list_tests = 'module'
253 elif args.list_classes:
254 args.list_tests = 'class'
255 elif args.list_tests:
256 args.list_tests = 'name'
257
Brad Bishopf86d0552018-12-04 14:18:15 -0800258 self.tc_kwargs['init']['td'] = bbvars
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500259 self.tc_kwargs['init']['machines'] = self._get_available_machines()
260
261 builddir = os.environ.get("BUILDDIR")
262 self.tc_kwargs['init']['config_paths'] = {}
Andrew Geissler82c905d2020-04-13 13:39:40 -0500263 self.tc_kwargs['init']['config_paths']['testlayer_path'] = get_test_layer()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500264 self.tc_kwargs['init']['config_paths']['builddir'] = builddir
Andrew Geissler82c905d2020-04-13 13:39:40 -0500265 self.tc_kwargs['init']['config_paths']['localconf'] = os.path.join(builddir, "conf/local.conf")
266 self.tc_kwargs['init']['config_paths']['bblayers'] = os.path.join(builddir, "conf/bblayers.conf")
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500267 self.tc_kwargs['init']['newbuilddir'] = args.newbuilddir
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600268 self.tc_kwargs['init']['keep_builddir'] = args.keep_builddir
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500269
Brad Bishop79641f22019-09-10 07:20:22 -0400270 def tag_filter(tags):
271 if args.exclude_tags:
272 if any(tag in args.exclude_tags for tag in tags):
273 return True
274 if args.select_tags:
275 if not tags or not any(tag in args.select_tags for tag in tags):
276 return True
277 return False
278
279 if args.select_tags or args.exclude_tags:
280 self.tc_kwargs['load']['tags_filter'] = tag_filter
281
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500282 self.tc_kwargs['run']['skips'] = args.skips
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800283 self.tc_kwargs['run']['processes'] = args.processes
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500284
285 def _pre_run(self):
286 def _check_required_env_variables(vars):
287 for var in vars:
288 if not os.environ.get(var):
289 self.tc.logger.error("%s is not set. Did you forget to source your build environment setup script?" % var)
290 raise OEQAPreRun
291
292 def _check_presence_meta_selftest():
293 builddir = os.environ.get("BUILDDIR")
294 if os.getcwd() != builddir:
295 self.tc.logger.info("Changing cwd to %s" % builddir)
296 os.chdir(builddir)
297
298 if not "meta-selftest" in self.tc.td["BBLAYERS"]:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800299 self.tc.logger.warning("meta-selftest layer not found in BBLAYERS, adding it")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500300 meta_selftestdir = os.path.join(
301 self.tc.td["BBLAYERS_FETCH_DIR"], 'meta-selftest')
302 if os.path.isdir(meta_selftestdir):
303 runCmd("bitbake-layers add-layer %s" %meta_selftestdir)
304 # reload data is needed because a meta-selftest layer was add
305 self.tc.td = get_bb_vars()
306 self.tc.config_paths['testlayer_path'] = get_test_layer()
307 else:
308 self.tc.logger.error("could not locate meta-selftest in:\n%s" % meta_selftestdir)
309 raise OEQAPreRun
310
311 def _add_layer_libs():
312 bbpath = self.tc.td['BBPATH'].split(':')
313 layer_libdirs = [p for p in (os.path.join(l, 'lib') \
314 for l in bbpath) if os.path.exists(p)]
315 if layer_libdirs:
316 self.tc.logger.info("Adding layer libraries:")
317 for l in layer_libdirs:
318 self.tc.logger.info("\t%s" % l)
319
320 sys.path.extend(layer_libdirs)
Brad Bishopf86d0552018-12-04 14:18:15 -0800321 importlib.reload(oeqa.selftest)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500322
323 _check_required_env_variables(["BUILDDIR"])
324 _check_presence_meta_selftest()
325
326 if "buildhistory.bbclass" in self.tc.td["BBINCLUDED"]:
327 self.tc.logger.error("You have buildhistory enabled already and this isn't recommended for selftest, please disable it first.")
328 raise OEQAPreRun
329
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800330 if "rm_work.bbclass" in self.tc.td["BBINCLUDED"]:
331 self.tc.logger.error("You have rm_work enabled which isn't recommended while running oe-selftest. Please disable it before continuing.")
332 raise OEQAPreRun
333
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500334 if "PRSERV_HOST" in self.tc.td:
335 self.tc.logger.error("Please unset PRSERV_HOST in order to run oe-selftest")
336 raise OEQAPreRun
337
338 if "SANITY_TESTED_DISTROS" in self.tc.td:
339 self.tc.logger.error("Please unset SANITY_TESTED_DISTROS in order to run oe-selftest")
340 raise OEQAPreRun
341
342 _add_layer_libs()
343
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800344 self.tc.logger.info("Running bitbake -e to test the configuration is valid/parsable")
345 runCmd("bitbake -e")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500346
Brad Bishopf86d0552018-12-04 14:18:15 -0800347 def get_json_result_dir(self, args):
348 json_result_dir = os.path.join(self.tc.td["LOG_DIR"], 'oeqa')
349 if "OEQA_JSON_RESULT_DIR" in self.tc.td:
350 json_result_dir = self.tc.td["OEQA_JSON_RESULT_DIR"]
351
352 return json_result_dir
353
354 def get_configuration(self, args):
355 import platform
356 from oeqa.utils.metadata import metadata_from_bb
357 metadata = metadata_from_bb()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600358 oeselftest_metadata = get_oeselftest_metadata(args)
Brad Bishopf86d0552018-12-04 14:18:15 -0800359 configuration = {'TEST_TYPE': 'oeselftest',
360 'STARTTIME': args.test_start_time,
361 'MACHINE': self.tc.td["MACHINE"],
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800362 'HOST_DISTRO': oe.lsb.distro_identifier().replace(' ', '-'),
Brad Bishopf86d0552018-12-04 14:18:15 -0800363 'HOST_NAME': metadata['hostname'],
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600364 'LAYERS': metadata['layers'],
365 'OESELFTEST_METADATA': oeselftest_metadata}
Brad Bishopf86d0552018-12-04 14:18:15 -0800366 return configuration
367
368 def get_result_id(self, configuration):
369 return '%s_%s_%s_%s' % (configuration['TEST_TYPE'], configuration['HOST_DISTRO'], configuration['MACHINE'], configuration['STARTTIME'])
370
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500371 def _internal_run(self, logger, args):
372 self.module_paths = self._get_cases_paths(
373 self.tc_kwargs['init']['td']['BBPATH'].split(':'))
374
375 self.tc = self._context_class(**self.tc_kwargs['init'])
376 try:
377 self.tc.loadTests(self.module_paths, **self.tc_kwargs['load'])
378 except OEQATestNotFound as ex:
379 logger.error(ex)
380 sys.exit(1)
381
382 if args.list_tests:
383 rc = self.tc.listTests(args.list_tests, **self.tc_kwargs['list'])
384 else:
385 self._pre_run()
386 rc = self.tc.runTests(**self.tc_kwargs['run'])
Brad Bishopf86d0552018-12-04 14:18:15 -0800387 configuration = self.get_configuration(args)
388 rc.logDetails(self.get_json_result_dir(args),
389 configuration,
390 self.get_result_id(configuration))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500391 rc.logSummary(self.name)
392
393 return rc
394
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500395 def run(self, logger, args):
396 self._process_args(logger, args)
397
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500398 rc = None
399 try:
400 if args.machine:
401 logger.info('Custom machine mode enabled. MACHINE set to %s' %
402 args.machine)
403
404 if args.machine == 'all':
405 results = []
406 for m in self.tc_kwargs['init']['machines']:
407 self.tc_kwargs['run']['machine'] = m
408 results.append(self._internal_run(logger, args))
409
410 # XXX: the oe-selftest script only needs to know if one
411 # machine run fails
412 for r in results:
413 rc = r
414 if not r.wasSuccessful():
415 break
416
417 else:
418 self.tc_kwargs['run']['machine'] = args.machine
419 return self._internal_run(logger, args)
420
421 else:
422 self.tc_kwargs['run']['machine'] = args.machine
423 rc = self._internal_run(logger, args)
424 finally:
425 config_paths = self.tc_kwargs['init']['config_paths']
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500426
427 output_link = os.path.join(os.path.dirname(args.output_log),
428 "%s-results.log" % self.name)
Brad Bishopf86d0552018-12-04 14:18:15 -0800429 if os.path.lexists(output_link):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500430 os.remove(output_link)
431 os.symlink(args.output_log, output_link)
432
433 return rc
434
435_executor_class = OESelftestTestContextExecutor