blob: 5e23ef0038f09da08ff3852eaade7305380fab44 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#!/usr/bin/env python
2
3# Copyright (c) 2013 Intel Corporation
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License version 2 as
7# published by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc.,
16# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18# DESCRIPTION
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050019# This script runs tests defined in meta/lib/oeqa/selftest/
Patrick Williamsc124f4f2015-09-15 14:41:29 -050020# It's purpose is to automate the testing of different bitbake tools.
21# To use it you just need to source your build environment setup script and
22# add the meta-selftest layer to your BBLAYERS.
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050023# Call the script as: "oe-selftest -a" to run all the tests in meta/lib/oeqa/selftest/
24# Call the script as: "oe-selftest -r <module>.<Class>.<method>" to run just a single test
25# E.g: "oe-selftest -r bblayers.BitbakeLayers" will run just the BitbakeLayers class from meta/lib/oeqa/selftest/bblayers.py
Patrick Williamsc124f4f2015-09-15 14:41:29 -050026
27
28import os
29import sys
30import unittest
31import logging
32import argparse
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050033import subprocess
34import time as t
35import re
36import fnmatch
Patrick Williamsc124f4f2015-09-15 14:41:29 -050037
38sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '/lib')
39import scriptpath
40scriptpath.add_bitbake_lib_path()
41scriptpath.add_oe_lib_path()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050042import argparse_oe
Patrick Williamsc124f4f2015-09-15 14:41:29 -050043
44import oeqa.selftest
45import oeqa.utils.ftools as ftools
46from oeqa.utils.commands import runCmd, get_bb_var, get_test_layer
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050047from oeqa.selftest.base import oeSelfTest, get_available_machines
Patrick Williamsc124f4f2015-09-15 14:41:29 -050048
49def logger_create():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050050 log_file = "oe-selftest-" + t.strftime("%Y-%m-%d_%H:%M:%S") + ".log"
51 if os.path.exists("oe-selftest.log"): os.remove("oe-selftest.log")
52 os.symlink(log_file, "oe-selftest.log")
53
Patrick Williamsc124f4f2015-09-15 14:41:29 -050054 log = logging.getLogger("selftest")
55 log.setLevel(logging.DEBUG)
56
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050057 fh = logging.FileHandler(filename=log_file, mode='w')
Patrick Williamsc124f4f2015-09-15 14:41:29 -050058 fh.setLevel(logging.DEBUG)
59
60 ch = logging.StreamHandler(sys.stdout)
61 ch.setLevel(logging.INFO)
62
63 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
64 fh.setFormatter(formatter)
65 ch.setFormatter(formatter)
66
67 log.addHandler(fh)
68 log.addHandler(ch)
69
70 return log
71
72log = logger_create()
73
74def get_args_parser():
75 description = "Script that runs unit tests agains bitbake and other Yocto related tools. The goal is to validate tools functionality and metadata integrity. Refer to https://wiki.yoctoproject.org/wiki/Oe-selftest for more information."
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050076 parser = argparse_oe.ArgumentParser(description=description)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050077 group = parser.add_mutually_exclusive_group(required=True)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050078 group.add_argument('-r', '--run-tests', required=False, action='store', nargs='*', dest="run_tests", default=None, help='Select what tests to run (modules, classes or test methods). Format should be: <module>.<class>.<test_method>')
79 group.add_argument('-a', '--run-all-tests', required=False, action="store_true", dest="run_all_tests", default=False, help='Run all (unhidden) tests')
80 group.add_argument('-m', '--list-modules', required=False, action="store_true", dest="list_modules", default=False, help='List all available test modules.')
Patrick Williamsc124f4f2015-09-15 14:41:29 -050081 group.add_argument('--list-classes', required=False, action="store_true", dest="list_allclasses", default=False, help='List all available test classes.')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050082 parser.add_argument('--coverage', action="store_true", help="Run code coverage when testing")
83 parser.add_argument('--coverage-source', dest="coverage_source", nargs="+", help="Specifiy the directories to take coverage from")
84 parser.add_argument('--coverage-include', dest="coverage_include", nargs="+", help="Specify extra patterns to include into the coverage measurement")
85 parser.add_argument('--coverage-omit', dest="coverage_omit", nargs="+", help="Specify with extra patterns to exclude from the coverage measurement")
86 group.add_argument('--run-tests-by', required=False, dest='run_tests_by', default=False, nargs='*',
87 help='run-tests-by <name|class|module|id|tag> <list of tests|classes|modules|ids|tags>')
88 group.add_argument('--list-tests-by', required=False, dest='list_tests_by', default=False, nargs='*',
89 help='list-tests-by <name|class|module|id|tag> <list of tests|classes|modules|ids|tags>')
90 group.add_argument('-l', '--list-tests', required=False, action="store_true", dest="list_tests", default=False,
91 help='List all available tests.')
92 group.add_argument('--list-tags', required=False, dest='list_tags', default=False, action="store_true",
93 help='List all tags that have been set to test cases.')
94 parser.add_argument('--machine', required=False, dest='machine', choices=['random', 'all'], default=None,
95 help='Run tests on different machines (random/all).')
Patrick Williamsc124f4f2015-09-15 14:41:29 -050096 return parser
97
98
99def preflight_check():
100
101 log.info("Checking that everything is in order before running the tests")
102
103 if not os.environ.get("BUILDDIR"):
104 log.error("BUILDDIR isn't set. Did you forget to source your build environment setup script?")
105 return False
106
107 builddir = os.environ.get("BUILDDIR")
108 if os.getcwd() != builddir:
109 log.info("Changing cwd to %s" % builddir)
110 os.chdir(builddir)
111
112 if not "meta-selftest" in get_bb_var("BBLAYERS"):
113 log.error("You don't seem to have the meta-selftest layer in BBLAYERS")
114 return False
115
116 log.info("Running bitbake -p")
117 runCmd("bitbake -p")
118
119 return True
120
121def add_include():
122 builddir = os.environ.get("BUILDDIR")
123 if "#include added by oe-selftest.py" \
124 not in ftools.read_file(os.path.join(builddir, "conf/local.conf")):
125 log.info("Adding: \"include selftest.inc\" in local.conf")
126 ftools.append_file(os.path.join(builddir, "conf/local.conf"), \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500127 "\n#include added by oe-selftest.py\ninclude machine.inc\ninclude selftest.inc")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500128
129 if "#include added by oe-selftest.py" \
130 not in ftools.read_file(os.path.join(builddir, "conf/bblayers.conf")):
131 log.info("Adding: \"include bblayers.inc\" in bblayers.conf")
132 ftools.append_file(os.path.join(builddir, "conf/bblayers.conf"), \
133 "\n#include added by oe-selftest.py\ninclude bblayers.inc")
134
135def remove_include():
136 builddir = os.environ.get("BUILDDIR")
137 if builddir is None:
138 return
139 if "#include added by oe-selftest.py" \
140 in ftools.read_file(os.path.join(builddir, "conf/local.conf")):
141 log.info("Removing the include from local.conf")
142 ftools.remove_from_file(os.path.join(builddir, "conf/local.conf"), \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500143 "\n#include added by oe-selftest.py\ninclude machine.inc\ninclude selftest.inc")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500144
145 if "#include added by oe-selftest.py" \
146 in ftools.read_file(os.path.join(builddir, "conf/bblayers.conf")):
147 log.info("Removing the include from bblayers.conf")
148 ftools.remove_from_file(os.path.join(builddir, "conf/bblayers.conf"), \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500149 "\n#include added by oe-selftest.py\ninclude bblayers.inc")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500150
151def remove_inc_files():
152 try:
153 os.remove(os.path.join(os.environ.get("BUILDDIR"), "conf/selftest.inc"))
154 for root, _, files in os.walk(get_test_layer()):
155 for f in files:
156 if f == 'test_recipe.inc':
157 os.remove(os.path.join(root, f))
158 except (AttributeError, OSError,) as e: # AttributeError may happen if BUILDDIR is not set
159 pass
160
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500161 for incl_file in ['conf/bblayers.inc', 'conf/machine.inc']:
162 try:
163 os.remove(os.path.join(os.environ.get("BUILDDIR"), incl_file))
164 except:
165 pass
166
167
168def get_tests_modules(include_hidden=False):
169 modules_list = list()
170 for modules_path in oeqa.selftest.__path__:
171 for (p, d, f) in os.walk(modules_path):
172 files = sorted([f for f in os.listdir(p) if f.endswith('.py') and not (f.startswith('_') and not include_hidden) and not f.startswith('__') and f != 'base.py'])
173 for f in files:
174 submodules = p.split("selftest")[-1]
175 module = ""
176 if submodules:
177 module = 'oeqa.selftest' + submodules.replace("/",".") + "." + f.split('.py')[0]
178 else:
179 module = 'oeqa.selftest.' + f.split('.py')[0]
180 if module not in modules_list:
181 modules_list.append(module)
182 return modules_list
183
184
185def get_tests(exclusive_modules=[], include_hidden=False):
186 test_modules = list()
187 for x in exclusive_modules:
188 test_modules.append('oeqa.selftest.' + x)
189 if not test_modules:
190 inc_hidden = include_hidden
191 test_modules = get_tests_modules(inc_hidden)
192
193 return test_modules
194
195
196class Tc:
197 def __init__(self, tcname, tcclass, tcmodule, tcid=None, tctag=None):
198 self.tcname = tcname
199 self.tcclass = tcclass
200 self.tcmodule = tcmodule
201 self.tcid = tcid
202 # A test case can have multiple tags (as tuples) otherwise str will suffice
203 self.tctag = tctag
204 self.fullpath = '.'.join(['oeqa', 'selftest', tcmodule, tcclass, tcname])
205
206
207def get_tests_from_module(tmod):
208 tlist = []
209 prefix = 'oeqa.selftest.'
210
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500211 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500212 import importlib
213 modlib = importlib.import_module(tmod)
214 for mod in vars(modlib).values():
215 if isinstance(mod, type(oeSelfTest)) and issubclass(mod, oeSelfTest) and mod is not oeSelfTest:
216 for test in dir(mod):
217 if test.startswith('test_') and hasattr(vars(mod)[test], '__call__'):
218 # Get test case id and feature tag
219 # NOTE: if testcase decorator or feature tag not set will throw error
220 try:
221 tid = vars(mod)[test].test_case
222 except:
223 print 'DEBUG: tc id missing for ' + str(test)
224 tid = None
225 try:
226 ttag = vars(mod)[test].tag__feature
227 except:
228 # print 'DEBUG: feature tag missing for ' + str(test)
229 ttag = None
230
231 # NOTE: for some reason lstrip() doesn't work for mod.__module__
232 tlist.append(Tc(test, mod.__name__, mod.__module__.replace(prefix, ''), tid, ttag))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500233 except:
234 pass
235
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500236 return tlist
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500237
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500238
239def get_all_tests():
240 # Get all the test modules (except the hidden ones)
241 testlist = []
242 tests_modules = get_tests_modules()
243 # Get all the tests from modules
244 for tmod in sorted(tests_modules):
245 testlist += get_tests_from_module(tmod)
246 return testlist
247
248
249def get_testsuite_by(criteria, keyword):
250 # Get a testsuite based on 'keyword'
251 # criteria: name, class, module, id, tag
252 # keyword: a list of tests, classes, modules, ids, tags
253
254 ts = []
255 all_tests = get_all_tests()
256
257 def get_matches(values):
258 # Get an item and return the ones that match with keyword(s)
259 # values: the list of items (names, modules, classes...)
260 result = []
261 remaining = values[:]
262 for key in keyword:
263 if key in remaining:
264 # Regular matching of exact item
265 result.append(key)
266 remaining.remove(key)
267 else:
268 # Wildcard matching
269 pattern = re.compile(fnmatch.translate(r"%s" % key))
270 added = [x for x in remaining if pattern.match(x)]
271 result.extend(added)
272 remaining = [x for x in remaining if x not in added]
273
274 return result
275
276 if criteria == 'name':
277 names = get_matches([ tc.tcname for tc in all_tests ])
278 ts = [ tc for tc in all_tests if tc.tcname in names ]
279
280 elif criteria == 'class':
281 classes = get_matches([ tc.tcclass for tc in all_tests ])
282 ts = [ tc for tc in all_tests if tc.tcclass in classes ]
283
284 elif criteria == 'module':
285 modules = get_matches([ tc.tcmodule for tc in all_tests ])
286 ts = [ tc for tc in all_tests if tc.tcmodule in modules ]
287
288 elif criteria == 'id':
289 ids = get_matches([ str(tc.tcid) for tc in all_tests ])
290 ts = [ tc for tc in all_tests if str(tc.tcid) in ids ]
291
292 elif criteria == 'tag':
293 values = set()
294 for tc in all_tests:
295 # tc can have multiple tags (as tuple) otherwise str will suffice
296 if isinstance(tc.tctag, tuple):
297 values |= { str(tag) for tag in tc.tctag }
298 else:
299 values.add(str(tc.tctag))
300
301 tags = get_matches(list(values))
302
303 for tc in all_tests:
304 for tag in tags:
305 if isinstance(tc.tctag, tuple) and tag in tc.tctag:
306 ts.append(tc)
307 elif tag == tc.tctag:
308 ts.append(tc)
309
310 # Remove duplicates from the list
311 ts = list(set(ts))
312
313 return ts
314
315
316def list_testsuite_by(criteria, keyword):
317 # Get a testsuite based on 'keyword'
318 # criteria: name, class, module, id, tag
319 # keyword: a list of tests, classes, modules, ids, tags
320
321 ts = sorted([ (tc.tcid, tc.tctag, tc.tcname, tc.tcclass, tc.tcmodule) for tc in get_testsuite_by(criteria, keyword) ])
322
323 print '%-4s\t%-20s\t%-60s\t%-25s\t%-20s' % ('id', 'tag', 'name', 'class', 'module')
324 print '_' * 150
325 for t in ts:
326 if isinstance(t[1], (tuple, list)):
327 print '%-4s\t%-20s\t%-60s\t%-25s\t%-20s' % (t[0], ', '.join(t[1]), t[2], t[3], t[4])
328 else:
329 print '%-4s\t%-20s\t%-60s\t%-25s\t%-20s' % t
330 print '_' * 150
331 print 'Filtering by:\t %s' % criteria
332 print 'Looking for:\t %s' % ', '.join(str(x) for x in keyword)
333 print 'Total found:\t %s' % len(ts)
334
335
336def list_tests():
337 # List all available oe-selftest tests
338
339 ts = get_all_tests()
340
341 print '%-4s\t%-20s\t%-60s\t%-25s\t%-20s' % ('id', 'tag', 'name', 'class', 'module')
342 print '_' * 150
343 for t in ts:
344 if isinstance(t.tctag, (tuple, list)):
345 print '%-4s\t%-20s\t%-60s\t%-25s\t%-20s' % (t.tcid, ', '.join(t.tctag), t.tcname, t.tcclass, t.tcmodule)
346 else:
347 print '%-4s\t%-20s\t%-60s\t%-25s\t%-20s' % (t.tcid, t.tctag, t.tcname, t.tcclass, t.tcmodule)
348 print '_' * 150
349 print 'Total found:\t %s' % len(ts)
350
351
352def list_tags():
353 # Get all tags set to test cases
354 # This is useful when setting tags to test cases
355 # The list of tags should be kept as minimal as possible
356 tags = set()
357 all_tests = get_all_tests()
358
359 for tc in all_tests:
360 if isinstance(tc.tctag, (tuple, list)):
361 tags.update(set(tc.tctag))
362 else:
363 tags.add(tc.tctag)
364
365 print 'Tags:\t%s' % ', '.join(str(x) for x in tags)
366
367def coverage_setup(coverage_source, coverage_include, coverage_omit):
368 """ Set up the coverage measurement for the testcases to be run """
369 import datetime
370 import subprocess
371 builddir = os.environ.get("BUILDDIR")
372 pokydir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
373 curcommit= subprocess.check_output(["git", "--git-dir", os.path.join(pokydir, ".git"), "rev-parse", "HEAD"])
374 coveragerc = "%s/.coveragerc" % builddir
375 data_file = "%s/.coverage." % builddir
376 data_file += datetime.datetime.now().strftime('%Y%m%dT%H%M%S')
377 if os.path.isfile(data_file):
378 os.remove(data_file)
379 with open(coveragerc, 'w') as cps:
380 cps.write("# Generated with command '%s'\n" % " ".join(sys.argv))
381 cps.write("# HEAD commit %s\n" % curcommit.strip())
382 cps.write("[run]\n")
383 cps.write("data_file = %s\n" % data_file)
384 cps.write("branch = True\n")
385 # Measure just BBLAYERS, scripts and bitbake folders
386 cps.write("source = \n")
387 if coverage_source:
388 for directory in coverage_source:
389 if not os.path.isdir(directory):
390 log.warn("Directory %s is not valid.", directory)
391 cps.write(" %s\n" % directory)
392 else:
393 for layer in get_bb_var('BBLAYERS').split():
394 cps.write(" %s\n" % layer)
395 cps.write(" %s\n" % os.path.dirname(os.path.realpath(__file__)))
396 cps.write(" %s\n" % os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))),'bitbake'))
397
398 if coverage_include:
399 cps.write("include = \n")
400 for pattern in coverage_include:
401 cps.write(" %s\n" % pattern)
402 if coverage_omit:
403 cps.write("omit = \n")
404 for pattern in coverage_omit:
405 cps.write(" %s\n" % pattern)
406
407 return coveragerc
408
409def coverage_report():
410 """ Loads the coverage data gathered and reports it back """
411 try:
412 # Coverage4 uses coverage.Coverage
413 from coverage import Coverage
414 except:
415 # Coverage under version 4 uses coverage.coverage
416 from coverage import coverage as Coverage
417
418 import cStringIO as StringIO
419 from coverage.misc import CoverageException
420
421 cov_output = StringIO.StringIO()
422 # Creating the coverage data with the setting from the configuration file
423 cov = Coverage(config_file = os.environ.get('COVERAGE_PROCESS_START'))
424 try:
425 # Load data from the data file specified in the configuration
426 cov.load()
427 # Store report data in a StringIO variable
428 cov.report(file = cov_output, show_missing=False)
429 log.info("\n%s" % cov_output.getvalue())
430 except CoverageException as e:
431 # Show problems with the reporting. Since Coverage4 not finding any data to report raises an exception
432 log.warn("%s" % str(e))
433 finally:
434 cov_output.close()
435
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500436
437def main():
438 parser = get_args_parser()
439 args = parser.parse_args()
440
441 # Add <layer>/lib to sys.path, so layers can add selftests
442 log.info("Running bitbake -e to get BBPATH")
443 bbpath = get_bb_var('BBPATH').split(':')
444 layer_libdirs = [p for p in (os.path.join(l, 'lib') for l in bbpath) if os.path.exists(p)]
445 sys.path.extend(layer_libdirs)
446 reload(oeqa.selftest)
447
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500448 if args.run_tests_by and len(args.run_tests_by) >= 2:
449 valid_options = ['name', 'class', 'module', 'id', 'tag']
450 if args.run_tests_by[0] not in valid_options:
451 print '--run-tests-by %s not a valid option. Choose one of <name|class|module|id|tag>.' % args.run_tests_by[0]
452 return 1
453 else:
454 criteria = args.run_tests_by[0]
455 keyword = args.run_tests_by[1:]
456 ts = sorted([ tc.fullpath for tc in get_testsuite_by(criteria, keyword) ])
457
458 if args.list_tests_by and len(args.list_tests_by) >= 2:
459 valid_options = ['name', 'class', 'module', 'id', 'tag']
460 if args.list_tests_by[0] not in valid_options:
461 print '--list-tests-by %s not a valid option. Choose one of <name|class|module|id|tag>.' % args.list_tests_by[0]
462 return 1
463 else:
464 criteria = args.list_tests_by[0]
465 keyword = args.list_tests_by[1:]
466 list_testsuite_by(criteria, keyword)
467
468 if args.list_tests:
469 list_tests()
470
471 if args.list_tags:
472 list_tags()
473
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500474 if args.list_allclasses:
475 args.list_modules = True
476
477 if args.list_modules:
478 log.info('Listing all available test modules:')
479 testslist = get_tests(include_hidden=True)
480 for test in testslist:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500481 module = test.split('oeqa.selftest.')[-1]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500482 info = ''
483 if module.startswith('_'):
484 info = ' (hidden)'
485 print module + info
486 if args.list_allclasses:
487 try:
488 import importlib
489 modlib = importlib.import_module(test)
490 for v in vars(modlib):
491 t = vars(modlib)[v]
492 if isinstance(t, type(oeSelfTest)) and issubclass(t, oeSelfTest) and t!=oeSelfTest:
493 print " --", v
494 for method in dir(t):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500495 if method.startswith("test_") and callable(vars(t)[method]):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500496 print " -- --", method
497
498 except (AttributeError, ImportError) as e:
499 print e
500 pass
501
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500502 if args.run_tests or args.run_all_tests or args.run_tests_by:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500503 if not preflight_check():
504 return 1
505
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500506 if args.run_tests_by:
507 testslist = ts
508 else:
509 testslist = get_tests(exclusive_modules=(args.run_tests or []), include_hidden=False)
510
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500511 suite = unittest.TestSuite()
512 loader = unittest.TestLoader()
513 loader.sortTestMethodsUsing = None
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500514 runner = unittest.TextTestRunner(verbosity=2, resultclass=buildResultClass(args))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500515 # we need to do this here, otherwise just loading the tests
516 # will take 2 minutes (bitbake -e calls)
517 oeSelfTest.testlayer_path = get_test_layer()
518 for test in testslist:
519 log.info("Loading tests from: %s" % test)
520 try:
521 suite.addTests(loader.loadTestsFromName(test))
522 except AttributeError as e:
523 log.error("Failed to import %s" % test)
524 log.error(e)
525 return 1
526 add_include()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500527
528 if args.machine:
529 # Custom machine sets only weak default values (??=) for MACHINE in machine.inc
530 # This let test cases that require a specific MACHINE to be able to override it, using (?= or =)
531 log.info('Custom machine mode enabled. MACHINE set to %s' % args.machine)
532 if args.machine == 'random':
533 os.environ['CUSTOMMACHINE'] = 'random'
534 result = runner.run(suite)
535 else: # all
536 machines = get_available_machines()
537 for m in machines:
538 log.info('Run tests with custom MACHINE set to: %s' % m)
539 os.environ['CUSTOMMACHINE'] = m
540 result = runner.run(suite)
541 else:
542 result = runner.run(suite)
543
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500544 log.info("Finished")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500545
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500546 if result.wasSuccessful():
547 return 0
548 else:
549 return 1
550
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500551def buildResultClass(args):
552 """Build a Result Class to use in the testcase execution"""
553 import site
554
555 class StampedResult(unittest.TextTestResult):
556 """
557 Custom TestResult that prints the time when a test starts. As oe-selftest
558 can take a long time (ie a few hours) to run, timestamps help us understand
559 what tests are taking a long time to execute.
560 If coverage is required, this class executes the coverage setup and reporting.
561 """
562 def startTest(self, test):
563 import time
564 self.stream.write(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + " - ")
565 super(StampedResult, self).startTest(test)
566
567 def startTestRun(self):
568 """ Setup coverage before running any testcase """
569
570 # variable holding the coverage configuration file allowing subprocess to be measured
571 self.coveragepth = None
572
573 # indicates the system if coverage is currently installed
574 self.coverage_installed = True
575
576 if args.coverage or args.coverage_source or args.coverage_include or args.coverage_omit:
577 try:
578 # check if user can do coverage
579 import coverage
580 except:
581 log.warn("python coverage is not installed. More info on https://pypi.python.org/pypi/coverage")
582 self.coverage_installed = False
583
584 if self.coverage_installed:
585 log.info("Coverage is enabled")
586
587 # In case the user has not set the variable COVERAGE_PROCESS_START,
588 # create a default one and export it. The COVERAGE_PROCESS_START
589 # value indicates where the coverage configuration file resides
590 # More info on https://pypi.python.org/pypi/coverage
591 if not os.environ.get('COVERAGE_PROCESS_START'):
592 os.environ['COVERAGE_PROCESS_START'] = coverage_setup(args.coverage_source, args.coverage_include, args.coverage_omit)
593
594 # Use default site.USER_SITE and write corresponding config file
595 site.ENABLE_USER_SITE = True
596 if not os.path.exists(site.USER_SITE):
597 os.makedirs(site.USER_SITE)
598 self.coveragepth = os.path.join(site.USER_SITE, "coverage.pth")
599 with open(self.coveragepth, 'w') as cps:
600 cps.write('import sys,site; sys.path.extend(site.getsitepackages()); import coverage; coverage.process_startup();')
601
602 def stopTestRun(self):
603 """ Report coverage data after the testcases are run """
604
605 if args.coverage or args.coverage_source or args.coverage_include or args.coverage_omit:
606 if self.coverage_installed:
607 with open(os.environ['COVERAGE_PROCESS_START']) as ccf:
608 log.info("Coverage configuration file (%s)" % os.environ.get('COVERAGE_PROCESS_START'))
609 log.info("===========================")
610 log.info("\n%s" % "".join(ccf.readlines()))
611
612 log.info("Coverage Report")
613 log.info("===============")
614 try:
615 coverage_report()
616 finally:
617 # remove the pth file
618 try:
619 os.remove(self.coveragepth)
620 except OSError:
621 log.warn("Expected temporal file from coverage is missing, ignoring removal.")
622
623 return StampedResult
624
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500625
626if __name__ == "__main__":
627 try:
628 ret = main()
629 except Exception:
630 ret = 1
631 import traceback
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500632 traceback.print_exc()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500633 finally:
634 remove_include()
635 remove_inc_files()
636 sys.exit(ret)