diff options
Diffstat (limited to 'meta/lib/oeqa/selftest/context.py')
-rw-r--r-- | meta/lib/oeqa/selftest/context.py | 148 |
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 | |||
16 | import oeqa | 16 | import oeqa |
17 | import oe | 17 | import oe |
18 | import bb.utils | 18 | import bb.utils |
19 | import bb.tinfoil | ||
19 | 20 | ||
20 | from oeqa.core.context import OETestContext, OETestContextExecutor | 21 | from oeqa.core.context import OETestContext, OETestContextExecutor |
21 | from oeqa.core.exception import OEQAPreRun, OEQATestNotFound | 22 | from oeqa.core.exception import OEQAPreRun, OEQATestNotFound |
22 | 23 | ||
23 | from oeqa.utils.commands import runCmd, get_bb_vars, get_test_layer | 24 | from oeqa.utils.commands import runCmd, get_bb_vars, get_test_layer |
24 | 25 | ||
26 | OESELFTEST_METADATA=["run_all_tests", "run_tests", "skips", "machine", "select_tags", "exclude_tags"] | ||
27 | |||
28 | def 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 | |||
25 | class NonConcurrentTestSuite(unittest.TestSuite): | 37 | class 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 | ||
40 | def removebuilddir(d): | 53 | def 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 |