blob: f2a5ba792f6346d61a437bb04d80ce1e7767d800 [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
Patrick Williams8e7b46e2023-05-01 14:19:06 -050019import bb.tinfoil
Brad Bishopd7bf8c12018-02-25 22:55:05 -050020
21from oeqa.core.context import OETestContext, OETestContextExecutor
22from oeqa.core.exception import OEQAPreRun, OEQATestNotFound
23
24from oeqa.utils.commands import runCmd, get_bb_vars, get_test_layer
25
Andrew Geissler6aa7eec2023-03-03 12:41:14 -060026OESELFTEST_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
Andrew Geissler475cb722020-07-10 16:00:51 -050037class NonConcurrentTestSuite(unittest.TestSuite):
38 def __init__(self, suite, processes, setupfunc, removefunc):
39 super().__init__([suite])
40 self.processes = processes
41 self.suite = suite
42 self.setupfunc = setupfunc
43 self.removefunc = removefunc
44
45 def run(self, result):
46 (builddir, newbuilddir) = self.setupfunc("-st", None, self.suite)
47 ret = super().run(result)
48 os.chdir(builddir)
Andrew Geisslerd1e89492021-02-12 15:35:20 -060049 if newbuilddir and ret.wasSuccessful() and self.removefunc:
Andrew Geissler475cb722020-07-10 16:00:51 -050050 self.removefunc(newbuilddir)
51
52def removebuilddir(d):
53 delay = 5
Andrew Geisslereff27472021-10-29 15:35:00 -050054 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 -050055 time.sleep(1)
56 delay = delay - 1
57 # Deleting these directories takes a lot of time, use autobuilder
58 # clobberdir if its available
59 clobberdir = os.path.expanduser("~/yocto-autobuilder-helper/janitor/clobberdir")
60 if os.path.exists(clobberdir):
61 try:
62 subprocess.check_call([clobberdir, d])
63 return
64 except subprocess.CalledProcessError:
65 pass
66 bb.utils.prunedir(d, ionice=True)
67
Brad Bishopd7bf8c12018-02-25 22:55:05 -050068class OESelftestTestContext(OETestContext):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060069 def __init__(self, td=None, logger=None, machines=None, config_paths=None, newbuilddir=None, keep_builddir=None):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050070 super(OESelftestTestContext, self).__init__(td, logger)
71
72 self.machines = machines
73 self.custommachine = None
74 self.config_paths = config_paths
Andrew Geissler4ed12e12020-06-05 18:00:41 -050075 self.newbuilddir = newbuilddir
Brad Bishopd7bf8c12018-02-25 22:55:05 -050076
Andrew Geisslerd1e89492021-02-12 15:35:20 -060077 if keep_builddir:
78 self.removebuilddir = None
79 else:
80 self.removebuilddir = removebuilddir
81
Andrew Geissler82c905d2020-04-13 13:39:40 -050082 def setup_builddir(self, suffix, selftestdir, suite):
Patrick Williams8e7b46e2023-05-01 14:19:06 -050083 # Get SSTATE_DIR from the parent build dir
84 with bb.tinfoil.Tinfoil(tracking=True) as tinfoil:
85 tinfoil.prepare(quiet=2, config_only=True)
86 d = tinfoil.config_data
87 sstatedir = str(d.getVar('SSTATE_DIR'))
88
Andrew Geissler82c905d2020-04-13 13:39:40 -050089 builddir = os.environ['BUILDDIR']
90 if not selftestdir:
91 selftestdir = get_test_layer()
Andrew Geissler4ed12e12020-06-05 18:00:41 -050092 if self.newbuilddir:
93 newbuilddir = os.path.join(self.newbuilddir, 'build' + suffix)
94 else:
95 newbuilddir = builddir + suffix
Andrew Geissler82c905d2020-04-13 13:39:40 -050096 newselftestdir = newbuilddir + "/meta-selftest"
97
98 if os.path.exists(newbuilddir):
99 self.logger.error("Build directory %s already exists, aborting" % newbuilddir)
100 sys.exit(1)
101
102 bb.utils.mkdirhier(newbuilddir)
103 oe.path.copytree(builddir + "/conf", newbuilddir + "/conf")
104 oe.path.copytree(builddir + "/cache", newbuilddir + "/cache")
105 oe.path.copytree(selftestdir, newselftestdir)
106
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500107 subprocess.check_output("git init; git add *; git commit -a -m 'initial'", cwd=newselftestdir, shell=True)
108
109 # Tried to used bitbake-layers add/remove but it requires recipe parsing and hence is too slow
110 subprocess.check_output("sed %s/conf/bblayers.conf -i -e 's#%s#%s#g'" % (newbuilddir, selftestdir, newselftestdir), cwd=newbuilddir, shell=True)
111
112 # Relative paths in BBLAYERS only works when the new build dir share the same ascending node
113 if self.newbuilddir:
114 bblayers = subprocess.check_output("bitbake-getvar --value BBLAYERS | tail -1", cwd=builddir, shell=True, text=True)
115 if '..' in bblayers:
116 bblayers_abspath = [os.path.abspath(path) for path in bblayers.split()]
117 with open("%s/conf/bblayers.conf" % newbuilddir, "a") as f:
118 newbblayers = "# new bblayers to be used by selftest in the new build dir '%s'\n" % newbuilddir
119 newbblayers += 'BBLAYERS = "%s"\n' % ' '.join(bblayers_abspath)
120 f.write(newbblayers)
121
Andrew Geissler82c905d2020-04-13 13:39:40 -0500122 for e in os.environ:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500123 if builddir + "/" in os.environ[e]:
124 os.environ[e] = os.environ[e].replace(builddir + "/", newbuilddir + "/")
125 if os.environ[e].endswith(builddir):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500126 os.environ[e] = os.environ[e].replace(builddir, newbuilddir)
127
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500128 # Set SSTATE_DIR to match the parent SSTATE_DIR
129 subprocess.check_output("echo 'SSTATE_DIR ?= \"%s\"' >> %s/conf/local.conf" % (sstatedir, newbuilddir), cwd=newbuilddir, shell=True)
130
Andrew Geissler82c905d2020-04-13 13:39:40 -0500131 os.chdir(newbuilddir)
132
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500133 def patch_test(t):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500134 if not hasattr(t, "tc"):
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500135 return
Andrew Geissler82c905d2020-04-13 13:39:40 -0500136 cp = t.tc.config_paths
137 for p in cp:
138 if selftestdir in cp[p] and newselftestdir not in cp[p]:
139 cp[p] = cp[p].replace(selftestdir, newselftestdir)
140 if builddir in cp[p] and newbuilddir not in cp[p]:
141 cp[p] = cp[p].replace(builddir, newbuilddir)
142
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500143 def patch_suite(s):
144 for x in s:
145 if isinstance(x, unittest.TestSuite):
146 patch_suite(x)
147 else:
148 patch_test(x)
149
150 patch_suite(suite)
151
Andrew Geissler82c905d2020-04-13 13:39:40 -0500152 return (builddir, newbuilddir)
153
154 def prepareSuite(self, suites, processes):
155 if processes:
156 from oeqa.core.utils.concurrencytest import ConcurrentTestSuite
157
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600158 return ConcurrentTestSuite(suites, processes, self.setup_builddir, self.removebuilddir)
Andrew Geissler82c905d2020-04-13 13:39:40 -0500159 else:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600160 return NonConcurrentTestSuite(suites, processes, self.setup_builddir, self.removebuilddir)
Andrew Geissler82c905d2020-04-13 13:39:40 -0500161
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800162 def runTests(self, processes=None, machine=None, skips=[]):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500163 if machine:
164 self.custommachine = machine
165 if machine == 'random':
166 self.custommachine = choice(self.machines)
167 self.logger.info('Run tests with custom MACHINE set to: %s' % \
168 self.custommachine)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800169 return super(OESelftestTestContext, self).runTests(processes, skips)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500170
171 def listTests(self, display_type, machine=None):
172 return super(OESelftestTestContext, self).listTests(display_type)
173
174class OESelftestTestContextExecutor(OETestContextExecutor):
175 _context_class = OESelftestTestContext
176 _script_executor = 'oe-selftest'
177
178 name = 'oe-selftest'
179 help = 'oe-selftest test component'
180 description = 'Executes selftest tests'
181
182 def register_commands(self, logger, parser):
183 group = parser.add_mutually_exclusive_group(required=True)
184
185 group.add_argument('-a', '--run-all-tests', default=False,
186 action="store_true", dest="run_all_tests",
187 help='Run all (unhidden) tests')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500188 group.add_argument('-r', '--run-tests', required=False, action='store',
189 nargs='+', dest="run_tests", default=None,
190 help='Select what tests to run (modules, classes or test methods). Format should be: <module>.<class>.<test_method>')
191
192 group.add_argument('-m', '--list-modules', required=False,
193 action="store_true", default=False,
194 help='List all available test modules.')
195 group.add_argument('--list-classes', required=False,
196 action="store_true", default=False,
197 help='List all available test classes.')
198 group.add_argument('-l', '--list-tests', required=False,
199 action="store_true", default=False,
200 help='List all available tests.')
201
Andrew Geissler517393d2023-01-13 08:55:19 -0600202 parser.add_argument('-R', '--skip-tests', required=False, action='store',
203 nargs='+', dest="skips", default=None,
204 help='Skip the tests specified. Format should be <module>[.<class>[.<test_method>]]')
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800205 parser.add_argument('-j', '--num-processes', dest='processes', action='store',
206 type=int, help="number of processes to execute in parallel with")
207
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500208 parser.add_argument('--machine', required=False, choices=['random', 'all'],
209 help='Run tests on different machines (random/all).')
Brad Bishop79641f22019-09-10 07:20:22 -0400210
Brad Bishopacc069e2019-09-13 06:48:36 -0400211 parser.add_argument('-t', '--select-tag', dest="select_tags",
212 action='append', default=None,
213 help='Filter all (unhidden) tests to any that match any of the specified tag(s).')
214 parser.add_argument('-T', '--exclude-tag', dest="exclude_tags",
215 action='append', default=None,
216 help='Exclude all (unhidden) tests that match any of the specified tag(s). (exclude applies before select)')
Brad Bishop79641f22019-09-10 07:20:22 -0400217
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600218 parser.add_argument('-K', '--keep-builddir', action='store_true',
219 help='Keep the test build directory even if all tests pass')
220
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500221 parser.add_argument('-B', '--newbuilddir', help='New build directory to use for tests.')
222 parser.add_argument('-v', '--verbose', action='store_true')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500223 parser.set_defaults(func=self.run)
224
225 def _get_available_machines(self):
226 machines = []
227
228 bbpath = self.tc_kwargs['init']['td']['BBPATH'].split(':')
229
230 for path in bbpath:
231 found_machines = glob.glob(os.path.join(path, 'conf', 'machine', '*.conf'))
232 if found_machines:
233 for i in found_machines:
234 # eg: '/home/<user>/poky/meta-intel/conf/machine/intel-core2-32.conf'
235 machines.append(os.path.splitext(os.path.basename(i))[0])
236
237 return machines
238
239 def _get_cases_paths(self, bbpath):
240 cases_paths = []
241 for layer in bbpath:
242 cases_dir = os.path.join(layer, 'lib', 'oeqa', 'selftest', 'cases')
243 if os.path.isdir(cases_dir):
244 cases_paths.append(cases_dir)
245 return cases_paths
246
247 def _process_args(self, logger, args):
Brad Bishopf86d0552018-12-04 14:18:15 -0800248 args.test_start_time = time.strftime("%Y%m%d%H%M%S")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500249 args.test_data_file = None
250 args.CASES_PATHS = None
251
Brad Bishopf86d0552018-12-04 14:18:15 -0800252 bbvars = get_bb_vars()
253 logdir = os.environ.get("BUILDDIR")
254 if 'LOG_DIR' in bbvars:
255 logdir = bbvars['LOG_DIR']
Brad Bishop19323692019-04-05 15:28:33 -0400256 bb.utils.mkdirhier(logdir)
Brad Bishopf86d0552018-12-04 14:18:15 -0800257 args.output_log = logdir + '/%s-results-%s.log' % (self.name, args.test_start_time)
258
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500259 super(OESelftestTestContextExecutor, self)._process_args(logger, args)
260
261 if args.list_modules:
262 args.list_tests = 'module'
263 elif args.list_classes:
264 args.list_tests = 'class'
265 elif args.list_tests:
266 args.list_tests = 'name'
267
Brad Bishopf86d0552018-12-04 14:18:15 -0800268 self.tc_kwargs['init']['td'] = bbvars
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500269 self.tc_kwargs['init']['machines'] = self._get_available_machines()
270
271 builddir = os.environ.get("BUILDDIR")
272 self.tc_kwargs['init']['config_paths'] = {}
Andrew Geissler82c905d2020-04-13 13:39:40 -0500273 self.tc_kwargs['init']['config_paths']['testlayer_path'] = get_test_layer()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500274 self.tc_kwargs['init']['config_paths']['builddir'] = builddir
Andrew Geissler82c905d2020-04-13 13:39:40 -0500275 self.tc_kwargs['init']['config_paths']['localconf'] = os.path.join(builddir, "conf/local.conf")
276 self.tc_kwargs['init']['config_paths']['bblayers'] = os.path.join(builddir, "conf/bblayers.conf")
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500277 self.tc_kwargs['init']['newbuilddir'] = args.newbuilddir
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600278 self.tc_kwargs['init']['keep_builddir'] = args.keep_builddir
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500279
Brad Bishop79641f22019-09-10 07:20:22 -0400280 def tag_filter(tags):
281 if args.exclude_tags:
282 if any(tag in args.exclude_tags for tag in tags):
283 return True
284 if args.select_tags:
285 if not tags or not any(tag in args.select_tags for tag in tags):
286 return True
287 return False
288
289 if args.select_tags or args.exclude_tags:
290 self.tc_kwargs['load']['tags_filter'] = tag_filter
291
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500292 self.tc_kwargs['run']['skips'] = args.skips
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800293 self.tc_kwargs['run']['processes'] = args.processes
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500294
295 def _pre_run(self):
296 def _check_required_env_variables(vars):
297 for var in vars:
298 if not os.environ.get(var):
299 self.tc.logger.error("%s is not set. Did you forget to source your build environment setup script?" % var)
300 raise OEQAPreRun
301
302 def _check_presence_meta_selftest():
303 builddir = os.environ.get("BUILDDIR")
304 if os.getcwd() != builddir:
305 self.tc.logger.info("Changing cwd to %s" % builddir)
306 os.chdir(builddir)
307
308 if not "meta-selftest" in self.tc.td["BBLAYERS"]:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800309 self.tc.logger.warning("meta-selftest layer not found in BBLAYERS, adding it")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500310 meta_selftestdir = os.path.join(
311 self.tc.td["BBLAYERS_FETCH_DIR"], 'meta-selftest')
312 if os.path.isdir(meta_selftestdir):
313 runCmd("bitbake-layers add-layer %s" %meta_selftestdir)
314 # reload data is needed because a meta-selftest layer was add
315 self.tc.td = get_bb_vars()
316 self.tc.config_paths['testlayer_path'] = get_test_layer()
317 else:
318 self.tc.logger.error("could not locate meta-selftest in:\n%s" % meta_selftestdir)
319 raise OEQAPreRun
320
321 def _add_layer_libs():
322 bbpath = self.tc.td['BBPATH'].split(':')
323 layer_libdirs = [p for p in (os.path.join(l, 'lib') \
324 for l in bbpath) if os.path.exists(p)]
325 if layer_libdirs:
326 self.tc.logger.info("Adding layer libraries:")
327 for l in layer_libdirs:
328 self.tc.logger.info("\t%s" % l)
329
330 sys.path.extend(layer_libdirs)
Brad Bishopf86d0552018-12-04 14:18:15 -0800331 importlib.reload(oeqa.selftest)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500332
333 _check_required_env_variables(["BUILDDIR"])
334 _check_presence_meta_selftest()
335
336 if "buildhistory.bbclass" in self.tc.td["BBINCLUDED"]:
337 self.tc.logger.error("You have buildhistory enabled already and this isn't recommended for selftest, please disable it first.")
338 raise OEQAPreRun
339
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800340 if "rm_work.bbclass" in self.tc.td["BBINCLUDED"]:
341 self.tc.logger.error("You have rm_work enabled which isn't recommended while running oe-selftest. Please disable it before continuing.")
342 raise OEQAPreRun
343
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500344 if "PRSERV_HOST" in self.tc.td:
345 self.tc.logger.error("Please unset PRSERV_HOST in order to run oe-selftest")
346 raise OEQAPreRun
347
348 if "SANITY_TESTED_DISTROS" in self.tc.td:
349 self.tc.logger.error("Please unset SANITY_TESTED_DISTROS in order to run oe-selftest")
350 raise OEQAPreRun
351
352 _add_layer_libs()
353
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800354 self.tc.logger.info("Running bitbake -e to test the configuration is valid/parsable")
355 runCmd("bitbake -e")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500356
Brad Bishopf86d0552018-12-04 14:18:15 -0800357 def get_json_result_dir(self, args):
358 json_result_dir = os.path.join(self.tc.td["LOG_DIR"], 'oeqa')
359 if "OEQA_JSON_RESULT_DIR" in self.tc.td:
360 json_result_dir = self.tc.td["OEQA_JSON_RESULT_DIR"]
361
362 return json_result_dir
363
364 def get_configuration(self, args):
365 import platform
366 from oeqa.utils.metadata import metadata_from_bb
367 metadata = metadata_from_bb()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600368 oeselftest_metadata = get_oeselftest_metadata(args)
Brad Bishopf86d0552018-12-04 14:18:15 -0800369 configuration = {'TEST_TYPE': 'oeselftest',
370 'STARTTIME': args.test_start_time,
371 'MACHINE': self.tc.td["MACHINE"],
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800372 'HOST_DISTRO': oe.lsb.distro_identifier().replace(' ', '-'),
Brad Bishopf86d0552018-12-04 14:18:15 -0800373 'HOST_NAME': metadata['hostname'],
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600374 'LAYERS': metadata['layers'],
375 'OESELFTEST_METADATA': oeselftest_metadata}
Brad Bishopf86d0552018-12-04 14:18:15 -0800376 return configuration
377
378 def get_result_id(self, configuration):
379 return '%s_%s_%s_%s' % (configuration['TEST_TYPE'], configuration['HOST_DISTRO'], configuration['MACHINE'], configuration['STARTTIME'])
380
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500381 def _internal_run(self, logger, args):
382 self.module_paths = self._get_cases_paths(
383 self.tc_kwargs['init']['td']['BBPATH'].split(':'))
384
385 self.tc = self._context_class(**self.tc_kwargs['init'])
386 try:
387 self.tc.loadTests(self.module_paths, **self.tc_kwargs['load'])
388 except OEQATestNotFound as ex:
389 logger.error(ex)
390 sys.exit(1)
391
392 if args.list_tests:
393 rc = self.tc.listTests(args.list_tests, **self.tc_kwargs['list'])
394 else:
395 self._pre_run()
396 rc = self.tc.runTests(**self.tc_kwargs['run'])
Brad Bishopf86d0552018-12-04 14:18:15 -0800397 configuration = self.get_configuration(args)
398 rc.logDetails(self.get_json_result_dir(args),
399 configuration,
400 self.get_result_id(configuration))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500401 rc.logSummary(self.name)
402
403 return rc
404
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500405 def run(self, logger, args):
406 self._process_args(logger, args)
407
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500408 rc = None
409 try:
410 if args.machine:
411 logger.info('Custom machine mode enabled. MACHINE set to %s' %
412 args.machine)
413
414 if args.machine == 'all':
415 results = []
416 for m in self.tc_kwargs['init']['machines']:
417 self.tc_kwargs['run']['machine'] = m
418 results.append(self._internal_run(logger, args))
419
420 # XXX: the oe-selftest script only needs to know if one
421 # machine run fails
422 for r in results:
423 rc = r
424 if not r.wasSuccessful():
425 break
426
427 else:
428 self.tc_kwargs['run']['machine'] = args.machine
429 return self._internal_run(logger, args)
430
431 else:
432 self.tc_kwargs['run']['machine'] = args.machine
433 rc = self._internal_run(logger, args)
434 finally:
435 config_paths = self.tc_kwargs['init']['config_paths']
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500436
437 output_link = os.path.join(os.path.dirname(args.output_log),
438 "%s-results.log" % self.name)
Brad Bishopf86d0552018-12-04 14:18:15 -0800439 if os.path.lexists(output_link):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500440 os.remove(output_link)
441 os.symlink(args.output_log, output_link)
442
443 return rc
444
445_executor_class = OESelftestTestContextExecutor