blob: 91e2dd28242f81492c27a41e2c6fe004213c644d [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
19# This script runs tests defined in meta/lib/selftest/
20# 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.
23# Call the script as: "oe-selftest" to run all the tests in in meta/lib/selftest/
24# Call the script as: "oe-selftest <module>.<Class>.<method>" to run just a single test
25# E.g: "oe-selftest bboutput.BitbakeLayers" will run just the BitbakeLayers class from meta/lib/selftest/bboutput.py
26
27
28import os
29import sys
30import unittest
31import logging
32import argparse
33
34sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '/lib')
35import scriptpath
36scriptpath.add_bitbake_lib_path()
37scriptpath.add_oe_lib_path()
38
39import oeqa.selftest
40import oeqa.utils.ftools as ftools
41from oeqa.utils.commands import runCmd, get_bb_var, get_test_layer
42from oeqa.selftest.base import oeSelfTest
43
44def logger_create():
45 log = logging.getLogger("selftest")
46 log.setLevel(logging.DEBUG)
47
48 fh = logging.FileHandler(filename='oe-selftest.log', mode='w')
49 fh.setLevel(logging.DEBUG)
50
51 ch = logging.StreamHandler(sys.stdout)
52 ch.setLevel(logging.INFO)
53
54 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
55 fh.setFormatter(formatter)
56 ch.setFormatter(formatter)
57
58 log.addHandler(fh)
59 log.addHandler(ch)
60
61 return log
62
63log = logger_create()
64
65def get_args_parser():
66 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."
67 parser = argparse.ArgumentParser(description=description)
68 group = parser.add_mutually_exclusive_group(required=True)
69 group.add_argument('--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>')
70 group.add_argument('--run-all-tests', required=False, action="store_true", dest="run_all_tests", default=False, help='Run all (unhidden) tests')
71 group.add_argument('--list-modules', required=False, action="store_true", dest="list_modules", default=False, help='List all available test modules.')
72 group.add_argument('--list-classes', required=False, action="store_true", dest="list_allclasses", default=False, help='List all available test classes.')
73 return parser
74
75
76def preflight_check():
77
78 log.info("Checking that everything is in order before running the tests")
79
80 if not os.environ.get("BUILDDIR"):
81 log.error("BUILDDIR isn't set. Did you forget to source your build environment setup script?")
82 return False
83
84 builddir = os.environ.get("BUILDDIR")
85 if os.getcwd() != builddir:
86 log.info("Changing cwd to %s" % builddir)
87 os.chdir(builddir)
88
89 if not "meta-selftest" in get_bb_var("BBLAYERS"):
90 log.error("You don't seem to have the meta-selftest layer in BBLAYERS")
91 return False
92
93 log.info("Running bitbake -p")
94 runCmd("bitbake -p")
95
96 return True
97
98def add_include():
99 builddir = os.environ.get("BUILDDIR")
100 if "#include added by oe-selftest.py" \
101 not in ftools.read_file(os.path.join(builddir, "conf/local.conf")):
102 log.info("Adding: \"include selftest.inc\" in local.conf")
103 ftools.append_file(os.path.join(builddir, "conf/local.conf"), \
104 "\n#include added by oe-selftest.py\ninclude selftest.inc")
105
106 if "#include added by oe-selftest.py" \
107 not in ftools.read_file(os.path.join(builddir, "conf/bblayers.conf")):
108 log.info("Adding: \"include bblayers.inc\" in bblayers.conf")
109 ftools.append_file(os.path.join(builddir, "conf/bblayers.conf"), \
110 "\n#include added by oe-selftest.py\ninclude bblayers.inc")
111
112def remove_include():
113 builddir = os.environ.get("BUILDDIR")
114 if builddir is None:
115 return
116 if "#include added by oe-selftest.py" \
117 in ftools.read_file(os.path.join(builddir, "conf/local.conf")):
118 log.info("Removing the include from local.conf")
119 ftools.remove_from_file(os.path.join(builddir, "conf/local.conf"), \
120 "#include added by oe-selftest.py\ninclude selftest.inc")
121
122 if "#include added by oe-selftest.py" \
123 in ftools.read_file(os.path.join(builddir, "conf/bblayers.conf")):
124 log.info("Removing the include from bblayers.conf")
125 ftools.remove_from_file(os.path.join(builddir, "conf/bblayers.conf"), \
126 "#include added by oe-selftest.py\ninclude bblayers.inc")
127
128def remove_inc_files():
129 try:
130 os.remove(os.path.join(os.environ.get("BUILDDIR"), "conf/selftest.inc"))
131 for root, _, files in os.walk(get_test_layer()):
132 for f in files:
133 if f == 'test_recipe.inc':
134 os.remove(os.path.join(root, f))
135 except (AttributeError, OSError,) as e: # AttributeError may happen if BUILDDIR is not set
136 pass
137
138 try:
139 os.remove(os.path.join(os.environ.get("BUILDDIR"), "conf/bblayers.inc"))
140 except:
141 pass
142
143def get_tests(exclusive_modules=[], include_hidden=False):
144 testslist = []
145 for x in exclusive_modules:
146 testslist.append('oeqa.selftest.' + x)
147 if not testslist:
148 for testpath in oeqa.selftest.__path__:
149 files = sorted([f for f in os.listdir(testpath) if f.endswith('.py') and not (f.startswith('_') and not include_hidden) and not f.startswith('__') and f != 'base.py'])
150 for f in files:
151 module = 'oeqa.selftest.' + f[:-3]
152 if module not in testslist:
153 testslist.append(module)
154
155 return testslist
156
157def main():
158 parser = get_args_parser()
159 args = parser.parse_args()
160
161 # Add <layer>/lib to sys.path, so layers can add selftests
162 log.info("Running bitbake -e to get BBPATH")
163 bbpath = get_bb_var('BBPATH').split(':')
164 layer_libdirs = [p for p in (os.path.join(l, 'lib') for l in bbpath) if os.path.exists(p)]
165 sys.path.extend(layer_libdirs)
166 reload(oeqa.selftest)
167
168 if args.list_allclasses:
169 args.list_modules = True
170
171 if args.list_modules:
172 log.info('Listing all available test modules:')
173 testslist = get_tests(include_hidden=True)
174 for test in testslist:
175 module = test.split('.')[-1]
176 info = ''
177 if module.startswith('_'):
178 info = ' (hidden)'
179 print module + info
180 if args.list_allclasses:
181 try:
182 import importlib
183 modlib = importlib.import_module(test)
184 for v in vars(modlib):
185 t = vars(modlib)[v]
186 if isinstance(t, type(oeSelfTest)) and issubclass(t, oeSelfTest) and t!=oeSelfTest:
187 print " --", v
188 for method in dir(t):
189 if method.startswith("test_"):
190 print " -- --", method
191
192 except (AttributeError, ImportError) as e:
193 print e
194 pass
195
196 if args.run_tests or args.run_all_tests:
197 if not preflight_check():
198 return 1
199
200 testslist = get_tests(exclusive_modules=(args.run_tests or []), include_hidden=False)
201 suite = unittest.TestSuite()
202 loader = unittest.TestLoader()
203 loader.sortTestMethodsUsing = None
204 runner = unittest.TextTestRunner(verbosity=2, resultclass=StampedResult)
205 # we need to do this here, otherwise just loading the tests
206 # will take 2 minutes (bitbake -e calls)
207 oeSelfTest.testlayer_path = get_test_layer()
208 for test in testslist:
209 log.info("Loading tests from: %s" % test)
210 try:
211 suite.addTests(loader.loadTestsFromName(test))
212 except AttributeError as e:
213 log.error("Failed to import %s" % test)
214 log.error(e)
215 return 1
216 add_include()
217 result = runner.run(suite)
218 log.info("Finished")
219 if result.wasSuccessful():
220 return 0
221 else:
222 return 1
223
224class StampedResult(unittest.TextTestResult):
225 """
226 Custom TestResult that prints the time when a test starts. As oe-selftest
227 can take a long time (ie a few hours) to run, timestamps help us understand
228 what tests are taking a long time to execute.
229 """
230 def startTest(self, test):
231 import time
232 self.stream.write(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + " - ")
233 super(StampedResult, self).startTest(test)
234
235if __name__ == "__main__":
236 try:
237 ret = main()
238 except Exception:
239 ret = 1
240 import traceback
241 traceback.print_exc(5)
242 finally:
243 remove_include()
244 remove_inc_files()
245 sys.exit(ret)