blob: dd3609c1d6f4b9555a0a21e99a322f90b9748e17 [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 Geissler475cb722020-07-10 16:00:51 -050025class NonConcurrentTestSuite(unittest.TestSuite):
26 def __init__(self, suite, processes, setupfunc, removefunc):
27 super().__init__([suite])
28 self.processes = processes
29 self.suite = suite
30 self.setupfunc = setupfunc
31 self.removefunc = removefunc
32
33 def run(self, result):
34 (builddir, newbuilddir) = self.setupfunc("-st", None, self.suite)
35 ret = super().run(result)
36 os.chdir(builddir)
37 if newbuilddir and ret.wasSuccessful():
38 self.removefunc(newbuilddir)
39
40def removebuilddir(d):
41 delay = 5
42 while delay and os.path.exists(d + "/bitbake.lock"):
43 time.sleep(1)
44 delay = delay - 1
45 # Deleting these directories takes a lot of time, use autobuilder
46 # clobberdir if its available
47 clobberdir = os.path.expanduser("~/yocto-autobuilder-helper/janitor/clobberdir")
48 if os.path.exists(clobberdir):
49 try:
50 subprocess.check_call([clobberdir, d])
51 return
52 except subprocess.CalledProcessError:
53 pass
54 bb.utils.prunedir(d, ionice=True)
55
Brad Bishopd7bf8c12018-02-25 22:55:05 -050056class OESelftestTestContext(OETestContext):
Andrew Geissler4ed12e12020-06-05 18:00:41 -050057 def __init__(self, td=None, logger=None, machines=None, config_paths=None, newbuilddir=None):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050058 super(OESelftestTestContext, self).__init__(td, logger)
59
60 self.machines = machines
61 self.custommachine = None
62 self.config_paths = config_paths
Andrew Geissler4ed12e12020-06-05 18:00:41 -050063 self.newbuilddir = newbuilddir
Brad Bishopd7bf8c12018-02-25 22:55:05 -050064
Andrew Geissler82c905d2020-04-13 13:39:40 -050065 def setup_builddir(self, suffix, selftestdir, suite):
66 builddir = os.environ['BUILDDIR']
67 if not selftestdir:
68 selftestdir = get_test_layer()
Andrew Geissler4ed12e12020-06-05 18:00:41 -050069 if self.newbuilddir:
70 newbuilddir = os.path.join(self.newbuilddir, 'build' + suffix)
71 else:
72 newbuilddir = builddir + suffix
Andrew Geissler82c905d2020-04-13 13:39:40 -050073 newselftestdir = newbuilddir + "/meta-selftest"
74
75 if os.path.exists(newbuilddir):
76 self.logger.error("Build directory %s already exists, aborting" % newbuilddir)
77 sys.exit(1)
78
79 bb.utils.mkdirhier(newbuilddir)
80 oe.path.copytree(builddir + "/conf", newbuilddir + "/conf")
81 oe.path.copytree(builddir + "/cache", newbuilddir + "/cache")
82 oe.path.copytree(selftestdir, newselftestdir)
83
84 for e in os.environ:
Andrew Geisslerc9f78652020-09-18 14:11:35 -050085 if builddir + "/" in os.environ[e]:
86 os.environ[e] = os.environ[e].replace(builddir + "/", newbuilddir + "/")
87 if os.environ[e].endswith(builddir):
Andrew Geissler82c905d2020-04-13 13:39:40 -050088 os.environ[e] = os.environ[e].replace(builddir, newbuilddir)
89
90 subprocess.check_output("git init; git add *; git commit -a -m 'initial'", cwd=newselftestdir, shell=True)
91
92 # Tried to used bitbake-layers add/remove but it requires recipe parsing and hence is too slow
93 subprocess.check_output("sed %s/conf/bblayers.conf -i -e 's#%s#%s#g'" % (newbuilddir, selftestdir, newselftestdir), cwd=newbuilddir, shell=True)
94
95 os.chdir(newbuilddir)
96
Andrew Geissler4ed12e12020-06-05 18:00:41 -050097 def patch_test(t):
Andrew Geissler82c905d2020-04-13 13:39:40 -050098 if not hasattr(t, "tc"):
Andrew Geissler4ed12e12020-06-05 18:00:41 -050099 return
Andrew Geissler82c905d2020-04-13 13:39:40 -0500100 cp = t.tc.config_paths
101 for p in cp:
102 if selftestdir in cp[p] and newselftestdir not in cp[p]:
103 cp[p] = cp[p].replace(selftestdir, newselftestdir)
104 if builddir in cp[p] and newbuilddir not in cp[p]:
105 cp[p] = cp[p].replace(builddir, newbuilddir)
106
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500107 def patch_suite(s):
108 for x in s:
109 if isinstance(x, unittest.TestSuite):
110 patch_suite(x)
111 else:
112 patch_test(x)
113
114 patch_suite(suite)
115
Andrew Geissler82c905d2020-04-13 13:39:40 -0500116 return (builddir, newbuilddir)
117
118 def prepareSuite(self, suites, processes):
119 if processes:
120 from oeqa.core.utils.concurrencytest import ConcurrentTestSuite
121
Andrew Geissler475cb722020-07-10 16:00:51 -0500122 return ConcurrentTestSuite(suites, processes, self.setup_builddir, removebuilddir)
Andrew Geissler82c905d2020-04-13 13:39:40 -0500123 else:
Andrew Geissler475cb722020-07-10 16:00:51 -0500124 return NonConcurrentTestSuite(suites, processes, self.setup_builddir, removebuilddir)
Andrew Geissler82c905d2020-04-13 13:39:40 -0500125
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800126 def runTests(self, processes=None, machine=None, skips=[]):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500127 if machine:
128 self.custommachine = machine
129 if machine == 'random':
130 self.custommachine = choice(self.machines)
131 self.logger.info('Run tests with custom MACHINE set to: %s' % \
132 self.custommachine)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800133 return super(OESelftestTestContext, self).runTests(processes, skips)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500134
135 def listTests(self, display_type, machine=None):
136 return super(OESelftestTestContext, self).listTests(display_type)
137
138class OESelftestTestContextExecutor(OETestContextExecutor):
139 _context_class = OESelftestTestContext
140 _script_executor = 'oe-selftest'
141
142 name = 'oe-selftest'
143 help = 'oe-selftest test component'
144 description = 'Executes selftest tests'
145
146 def register_commands(self, logger, parser):
147 group = parser.add_mutually_exclusive_group(required=True)
148
149 group.add_argument('-a', '--run-all-tests', default=False,
150 action="store_true", dest="run_all_tests",
151 help='Run all (unhidden) tests')
152 group.add_argument('-R', '--skip-tests', required=False, action='store',
153 nargs='+', dest="skips", default=None,
154 help='Run all (unhidden) tests except the ones specified. Format should be <module>[.<class>[.<test_method>]]')
155 group.add_argument('-r', '--run-tests', required=False, action='store',
156 nargs='+', dest="run_tests", default=None,
157 help='Select what tests to run (modules, classes or test methods). Format should be: <module>.<class>.<test_method>')
158
159 group.add_argument('-m', '--list-modules', required=False,
160 action="store_true", default=False,
161 help='List all available test modules.')
162 group.add_argument('--list-classes', required=False,
163 action="store_true", default=False,
164 help='List all available test classes.')
165 group.add_argument('-l', '--list-tests', required=False,
166 action="store_true", default=False,
167 help='List all available tests.')
168
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800169 parser.add_argument('-j', '--num-processes', dest='processes', action='store',
170 type=int, help="number of processes to execute in parallel with")
171
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500172 parser.add_argument('--machine', required=False, choices=['random', 'all'],
173 help='Run tests on different machines (random/all).')
Brad Bishop79641f22019-09-10 07:20:22 -0400174
Brad Bishopacc069e2019-09-13 06:48:36 -0400175 parser.add_argument('-t', '--select-tag', dest="select_tags",
176 action='append', default=None,
177 help='Filter all (unhidden) tests to any that match any of the specified tag(s).')
178 parser.add_argument('-T', '--exclude-tag', dest="exclude_tags",
179 action='append', default=None,
180 help='Exclude all (unhidden) tests that match any of the specified tag(s). (exclude applies before select)')
Brad Bishop79641f22019-09-10 07:20:22 -0400181
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500182 parser.add_argument('-B', '--newbuilddir', help='New build directory to use for tests.')
183 parser.add_argument('-v', '--verbose', action='store_true')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500184 parser.set_defaults(func=self.run)
185
186 def _get_available_machines(self):
187 machines = []
188
189 bbpath = self.tc_kwargs['init']['td']['BBPATH'].split(':')
190
191 for path in bbpath:
192 found_machines = glob.glob(os.path.join(path, 'conf', 'machine', '*.conf'))
193 if found_machines:
194 for i in found_machines:
195 # eg: '/home/<user>/poky/meta-intel/conf/machine/intel-core2-32.conf'
196 machines.append(os.path.splitext(os.path.basename(i))[0])
197
198 return machines
199
200 def _get_cases_paths(self, bbpath):
201 cases_paths = []
202 for layer in bbpath:
203 cases_dir = os.path.join(layer, 'lib', 'oeqa', 'selftest', 'cases')
204 if os.path.isdir(cases_dir):
205 cases_paths.append(cases_dir)
206 return cases_paths
207
208 def _process_args(self, logger, args):
Brad Bishopf86d0552018-12-04 14:18:15 -0800209 args.test_start_time = time.strftime("%Y%m%d%H%M%S")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500210 args.test_data_file = None
211 args.CASES_PATHS = None
212
Brad Bishopf86d0552018-12-04 14:18:15 -0800213 bbvars = get_bb_vars()
214 logdir = os.environ.get("BUILDDIR")
215 if 'LOG_DIR' in bbvars:
216 logdir = bbvars['LOG_DIR']
Brad Bishop19323692019-04-05 15:28:33 -0400217 bb.utils.mkdirhier(logdir)
Brad Bishopf86d0552018-12-04 14:18:15 -0800218 args.output_log = logdir + '/%s-results-%s.log' % (self.name, args.test_start_time)
219
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500220 super(OESelftestTestContextExecutor, self)._process_args(logger, args)
221
222 if args.list_modules:
223 args.list_tests = 'module'
224 elif args.list_classes:
225 args.list_tests = 'class'
226 elif args.list_tests:
227 args.list_tests = 'name'
228
Brad Bishopf86d0552018-12-04 14:18:15 -0800229 self.tc_kwargs['init']['td'] = bbvars
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500230 self.tc_kwargs['init']['machines'] = self._get_available_machines()
231
232 builddir = os.environ.get("BUILDDIR")
233 self.tc_kwargs['init']['config_paths'] = {}
Andrew Geissler82c905d2020-04-13 13:39:40 -0500234 self.tc_kwargs['init']['config_paths']['testlayer_path'] = get_test_layer()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500235 self.tc_kwargs['init']['config_paths']['builddir'] = builddir
Andrew Geissler82c905d2020-04-13 13:39:40 -0500236 self.tc_kwargs['init']['config_paths']['localconf'] = os.path.join(builddir, "conf/local.conf")
237 self.tc_kwargs['init']['config_paths']['bblayers'] = os.path.join(builddir, "conf/bblayers.conf")
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500238 self.tc_kwargs['init']['newbuilddir'] = args.newbuilddir
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500239
Brad Bishop79641f22019-09-10 07:20:22 -0400240 def tag_filter(tags):
241 if args.exclude_tags:
242 if any(tag in args.exclude_tags for tag in tags):
243 return True
244 if args.select_tags:
245 if not tags or not any(tag in args.select_tags for tag in tags):
246 return True
247 return False
248
249 if args.select_tags or args.exclude_tags:
250 self.tc_kwargs['load']['tags_filter'] = tag_filter
251
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500252 self.tc_kwargs['run']['skips'] = args.skips
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800253 self.tc_kwargs['run']['processes'] = args.processes
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500254
255 def _pre_run(self):
256 def _check_required_env_variables(vars):
257 for var in vars:
258 if not os.environ.get(var):
259 self.tc.logger.error("%s is not set. Did you forget to source your build environment setup script?" % var)
260 raise OEQAPreRun
261
262 def _check_presence_meta_selftest():
263 builddir = os.environ.get("BUILDDIR")
264 if os.getcwd() != builddir:
265 self.tc.logger.info("Changing cwd to %s" % builddir)
266 os.chdir(builddir)
267
268 if not "meta-selftest" in self.tc.td["BBLAYERS"]:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800269 self.tc.logger.warning("meta-selftest layer not found in BBLAYERS, adding it")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500270 meta_selftestdir = os.path.join(
271 self.tc.td["BBLAYERS_FETCH_DIR"], 'meta-selftest')
272 if os.path.isdir(meta_selftestdir):
273 runCmd("bitbake-layers add-layer %s" %meta_selftestdir)
274 # reload data is needed because a meta-selftest layer was add
275 self.tc.td = get_bb_vars()
276 self.tc.config_paths['testlayer_path'] = get_test_layer()
277 else:
278 self.tc.logger.error("could not locate meta-selftest in:\n%s" % meta_selftestdir)
279 raise OEQAPreRun
280
281 def _add_layer_libs():
282 bbpath = self.tc.td['BBPATH'].split(':')
283 layer_libdirs = [p for p in (os.path.join(l, 'lib') \
284 for l in bbpath) if os.path.exists(p)]
285 if layer_libdirs:
286 self.tc.logger.info("Adding layer libraries:")
287 for l in layer_libdirs:
288 self.tc.logger.info("\t%s" % l)
289
290 sys.path.extend(layer_libdirs)
Brad Bishopf86d0552018-12-04 14:18:15 -0800291 importlib.reload(oeqa.selftest)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500292
293 _check_required_env_variables(["BUILDDIR"])
294 _check_presence_meta_selftest()
295
296 if "buildhistory.bbclass" in self.tc.td["BBINCLUDED"]:
297 self.tc.logger.error("You have buildhistory enabled already and this isn't recommended for selftest, please disable it first.")
298 raise OEQAPreRun
299
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800300 if "rm_work.bbclass" in self.tc.td["BBINCLUDED"]:
301 self.tc.logger.error("You have rm_work enabled which isn't recommended while running oe-selftest. Please disable it before continuing.")
302 raise OEQAPreRun
303
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500304 if "PRSERV_HOST" in self.tc.td:
305 self.tc.logger.error("Please unset PRSERV_HOST in order to run oe-selftest")
306 raise OEQAPreRun
307
308 if "SANITY_TESTED_DISTROS" in self.tc.td:
309 self.tc.logger.error("Please unset SANITY_TESTED_DISTROS in order to run oe-selftest")
310 raise OEQAPreRun
311
312 _add_layer_libs()
313
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800314 self.tc.logger.info("Running bitbake -e to test the configuration is valid/parsable")
315 runCmd("bitbake -e")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500316
Brad Bishopf86d0552018-12-04 14:18:15 -0800317 def get_json_result_dir(self, args):
318 json_result_dir = os.path.join(self.tc.td["LOG_DIR"], 'oeqa')
319 if "OEQA_JSON_RESULT_DIR" in self.tc.td:
320 json_result_dir = self.tc.td["OEQA_JSON_RESULT_DIR"]
321
322 return json_result_dir
323
324 def get_configuration(self, args):
325 import platform
326 from oeqa.utils.metadata import metadata_from_bb
327 metadata = metadata_from_bb()
328 configuration = {'TEST_TYPE': 'oeselftest',
329 'STARTTIME': args.test_start_time,
330 'MACHINE': self.tc.td["MACHINE"],
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800331 'HOST_DISTRO': oe.lsb.distro_identifier().replace(' ', '-'),
Brad Bishopf86d0552018-12-04 14:18:15 -0800332 'HOST_NAME': metadata['hostname'],
333 'LAYERS': metadata['layers']}
334 return configuration
335
336 def get_result_id(self, configuration):
337 return '%s_%s_%s_%s' % (configuration['TEST_TYPE'], configuration['HOST_DISTRO'], configuration['MACHINE'], configuration['STARTTIME'])
338
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500339 def _internal_run(self, logger, args):
340 self.module_paths = self._get_cases_paths(
341 self.tc_kwargs['init']['td']['BBPATH'].split(':'))
342
343 self.tc = self._context_class(**self.tc_kwargs['init'])
344 try:
345 self.tc.loadTests(self.module_paths, **self.tc_kwargs['load'])
346 except OEQATestNotFound as ex:
347 logger.error(ex)
348 sys.exit(1)
349
350 if args.list_tests:
351 rc = self.tc.listTests(args.list_tests, **self.tc_kwargs['list'])
352 else:
353 self._pre_run()
354 rc = self.tc.runTests(**self.tc_kwargs['run'])
Brad Bishopf86d0552018-12-04 14:18:15 -0800355 configuration = self.get_configuration(args)
356 rc.logDetails(self.get_json_result_dir(args),
357 configuration,
358 self.get_result_id(configuration))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500359 rc.logSummary(self.name)
360
361 return rc
362
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500363 def run(self, logger, args):
364 self._process_args(logger, args)
365
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500366 rc = None
367 try:
368 if args.machine:
369 logger.info('Custom machine mode enabled. MACHINE set to %s' %
370 args.machine)
371
372 if args.machine == 'all':
373 results = []
374 for m in self.tc_kwargs['init']['machines']:
375 self.tc_kwargs['run']['machine'] = m
376 results.append(self._internal_run(logger, args))
377
378 # XXX: the oe-selftest script only needs to know if one
379 # machine run fails
380 for r in results:
381 rc = r
382 if not r.wasSuccessful():
383 break
384
385 else:
386 self.tc_kwargs['run']['machine'] = args.machine
387 return self._internal_run(logger, args)
388
389 else:
390 self.tc_kwargs['run']['machine'] = args.machine
391 rc = self._internal_run(logger, args)
392 finally:
393 config_paths = self.tc_kwargs['init']['config_paths']
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500394
395 output_link = os.path.join(os.path.dirname(args.output_log),
396 "%s-results.log" % self.name)
Brad Bishopf86d0552018-12-04 14:18:15 -0800397 if os.path.lexists(output_link):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500398 os.remove(output_link)
399 os.symlink(args.output_log, output_link)
400
401 return rc
402
403_executor_class = OESelftestTestContextExecutor