diff options
Diffstat (limited to 'bitbake/lib/toaster/contrib/tts/runner.py')
-rwxr-xr-x | bitbake/lib/toaster/contrib/tts/runner.py | 222 |
1 files changed, 0 insertions, 222 deletions
diff --git a/bitbake/lib/toaster/contrib/tts/runner.py b/bitbake/lib/toaster/contrib/tts/runner.py deleted file mode 100755 index d01386acfa..0000000000 --- a/bitbake/lib/toaster/contrib/tts/runner.py +++ /dev/null | |||
@@ -1,222 +0,0 @@ | |||
1 | #!/usr/bin/python | ||
2 | |||
3 | # ex:ts=4:sw=4:sts=4:et | ||
4 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
5 | # | ||
6 | # Copyright (C) 2015 Alexandru Damian for Intel Corp. | ||
7 | # | ||
8 | # This program is free software; you can redistribute it and/or modify | ||
9 | # it under the terms of the GNU General Public License version 2 as | ||
10 | # published by the Free Software Foundation. | ||
11 | # | ||
12 | # This program is distributed in the hope that it will be useful, | ||
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | # GNU General Public License for more details. | ||
16 | # | ||
17 | # You should have received a copy of the GNU General Public License along | ||
18 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
20 | |||
21 | |||
22 | # This is the main test execution controller. It is designed to be run | ||
23 | # manually from the command line, or to be called from a different program | ||
24 | # that schedules test execution. | ||
25 | # | ||
26 | # Execute runner.py -h for help. | ||
27 | |||
28 | |||
29 | |||
30 | from __future__ import print_function | ||
31 | import sys, os | ||
32 | import unittest, importlib | ||
33 | import logging, pprint, json | ||
34 | import re | ||
35 | from shellutils import ShellCmdException, mkdirhier, run_shell_cmd | ||
36 | |||
37 | import config | ||
38 | |||
39 | # we also log to a file, in addition to console, because our output is important | ||
40 | __log_file_name__ = os.path.join(os.path.dirname(__file__), "log/tts_%d.log" % config.OWN_PID) | ||
41 | mkdirhier(os.path.dirname(__log_file_name__)) | ||
42 | __log_file__ = open(__log_file_name__, "w") | ||
43 | __file_handler__ = logging.StreamHandler(__log_file__) | ||
44 | __file_handler__.setFormatter(logging.Formatter("%(asctime)s %(levelname)s: %(message)s")) | ||
45 | |||
46 | config.logger.addHandler(__file_handler__) | ||
47 | |||
48 | # set up log directory | ||
49 | try: | ||
50 | if not os.path.exists(config.LOGDIR): | ||
51 | os.mkdir(config.LOGDIR) | ||
52 | else: | ||
53 | if not os.path.isdir(config.LOGDIR): | ||
54 | raise Exception("Expected log dir '%s' is not actually a directory." % config.LOGDIR) | ||
55 | except OSError as exc: | ||
56 | raise exc | ||
57 | |||
58 | # creates the under-test-branch as a separate directory | ||
59 | def set_up_test_branch(settings, branch_name): | ||
60 | testdir = "%s/%s.%d" % (settings['workdir'], config.TEST_DIR_NAME, config.OWN_PID) | ||
61 | |||
62 | # creates the host dir | ||
63 | if os.path.exists(testdir): | ||
64 | raise Exception("Test dir '%s'is already there, aborting" % testdir) | ||
65 | |||
66 | # may raise OSError, is to be handled by the caller | ||
67 | os.makedirs(testdir) | ||
68 | |||
69 | |||
70 | # copies over the .git from the localclone | ||
71 | run_shell_cmd("cp -a '%s'/.git '%s'" % (settings['localclone'], testdir)) | ||
72 | |||
73 | # add the remote if it doesn't exist | ||
74 | crt_remotes = run_shell_cmd("git remote -v", cwd=testdir) | ||
75 | remotes = [word for line in crt_remotes.split("\n") for word in line.split()] | ||
76 | if not config.CONTRIB_REPO in remotes: | ||
77 | remote_name = "tts_contrib" | ||
78 | run_shell_cmd("git remote add %s %s" % (remote_name, config.CONTRIB_REPO), cwd=testdir) | ||
79 | else: | ||
80 | remote_name = remotes[remotes.index(config.CONTRIB_REPO) - 1] | ||
81 | |||
82 | # do the fetch | ||
83 | run_shell_cmd("git fetch %s -p" % remote_name, cwd=testdir) | ||
84 | |||
85 | # do the checkout | ||
86 | run_shell_cmd("git checkout origin/master && git branch -D %s; git checkout %s/%s -b %s && git reset --hard" % (branch_name, remote_name, branch_name, branch_name), cwd=testdir) | ||
87 | |||
88 | return testdir | ||
89 | |||
90 | |||
91 | def __search_for_tests(): | ||
92 | # we find all classes that can run, and run them | ||
93 | tests = [] | ||
94 | for _, _, files_list in os.walk(os.path.dirname(os.path.abspath(__file__))): | ||
95 | for module_file in [f[:-3] for f in files_list if f.endswith(".py") and not f.startswith("__init__")]: | ||
96 | config.logger.debug("Inspecting module %s", module_file) | ||
97 | current_module = importlib.import_module(module_file) | ||
98 | crtclass_names = vars(current_module) | ||
99 | for name in crtclass_names: | ||
100 | tested_value = crtclass_names[name] | ||
101 | if isinstance(tested_value, type(unittest.TestCase)) and issubclass(tested_value, unittest.TestCase): | ||
102 | tests.append((module_file, name)) | ||
103 | break | ||
104 | return tests | ||
105 | |||
106 | |||
107 | # boilerplate to self discover tests and run them | ||
108 | def execute_tests(dir_under_test, testname): | ||
109 | |||
110 | if testname is not None and "." in testname: | ||
111 | tests = [] | ||
112 | tests.append(tuple(testname.split(".", 2))) | ||
113 | else: | ||
114 | tests = __search_for_tests() | ||
115 | |||
116 | # let's move to the directory under test | ||
117 | crt_dir = os.getcwd() | ||
118 | os.chdir(dir_under_test) | ||
119 | |||
120 | # execute each module | ||
121 | # pylint: disable=broad-except | ||
122 | # we disable the broad-except because we want to actually catch all possible exceptions | ||
123 | try: | ||
124 | # sorting the tests by the numeric order in the class name | ||
125 | tests = sorted(tests, key=lambda x: int(re.search(r"[0-9]+", x[1]).group(0))) | ||
126 | config.logger.debug("Discovered test clases: %s", pprint.pformat(tests)) | ||
127 | unittest.installHandler() | ||
128 | suite = unittest.TestSuite() | ||
129 | loader = unittest.TestLoader() | ||
130 | result = unittest.TestResult() | ||
131 | result.failfast = True | ||
132 | for module_file, test_name in tests: | ||
133 | suite.addTest(loader.loadTestsFromName("%s.%s" % (module_file, test_name))) | ||
134 | config.logger.info("Running %d test(s)", suite.countTestCases()) | ||
135 | suite.run(result) | ||
136 | |||
137 | for error in result.errors: | ||
138 | config.logger.error("Exception on test: %s\n%s", error[0], | ||
139 | "\n".join(["-- %s" % x for x in error[1].split("\n")])) | ||
140 | |||
141 | for failure in result.failures: | ||
142 | config.logger.error("Failed test: %s:\n%s\n", failure[0], | ||
143 | "\n".join(["-- %s" % x for x in failure[1].split("\n")])) | ||
144 | |||
145 | config.logger.info("Test results: %d ran, %d errors, %d failures", result.testsRun, len(result.errors), len(result.failures)) | ||
146 | |||
147 | except Exception as exc: | ||
148 | import traceback | ||
149 | config.logger.error("Exception while running test. Tracedump: \n%s", traceback.format_exc()) | ||
150 | finally: | ||
151 | os.chdir(crt_dir) | ||
152 | return len(result.failures) | ||
153 | |||
154 | # verify that we had a branch-under-test name as parameter | ||
155 | def validate_args(): | ||
156 | from optparse import OptionParser | ||
157 | parser = OptionParser(usage="usage: %prog [options] branch_under_test") | ||
158 | |||
159 | parser.add_option("-t", "--test-dir", dest="testdir", default=None, help="Use specified directory to run tests, inhibits the checkout.") | ||
160 | parser.add_option("-s", "--single", dest="singletest", default=None, help="Run only the specified test") | ||
161 | |||
162 | (options, args) = parser.parse_args() | ||
163 | if len(args) < 1: | ||
164 | raise Exception("Please specify the branch to run on. Use option '-h' when in doubt.") | ||
165 | return (options, args) | ||
166 | |||
167 | |||
168 | |||
169 | |||
170 | # load the configuration options | ||
171 | def read_settings(): | ||
172 | if not os.path.exists(config.SETTINGS_FILE) or not os.path.isfile(config.SETTINGS_FILE): | ||
173 | raise Exception("Config file '%s' cannot be openend" % config.SETTINGS_FILE) | ||
174 | return json.loads(open(config.SETTINGS_FILE, "r").read()) | ||
175 | |||
176 | |||
177 | # cleanup ! | ||
178 | def clean_up(testdir): | ||
179 | run_shell_cmd("rm -rf -- '%s'" % testdir) | ||
180 | |||
181 | def dump_info(settings, options, args): | ||
182 | """ detailed information about current run configuration, for debugging purposes. | ||
183 | """ | ||
184 | config.logger.debug("Settings:\n%s\nOptions:\n%s\nArguments:\n%s\n", settings, options, args) | ||
185 | |||
186 | def main(): | ||
187 | (options, args) = validate_args() | ||
188 | |||
189 | settings = read_settings() | ||
190 | need_cleanup = False | ||
191 | |||
192 | # dump debug info | ||
193 | dump_info(settings, options, args) | ||
194 | |||
195 | testdir = None | ||
196 | no_failures = 1 | ||
197 | try: | ||
198 | if options.testdir is not None and os.path.exists(options.testdir): | ||
199 | testdir = os.path.abspath(options.testdir) | ||
200 | config.logger.info("No checkout, using %s", testdir) | ||
201 | else: | ||
202 | need_cleanup = True | ||
203 | testdir = set_up_test_branch(settings, args[0]) # we expect a branch name as first argument | ||
204 | |||
205 | config.TESTDIR = testdir # we let tests know where to run | ||
206 | |||
207 | # ensure that the test dir only contains no *.pyc leftovers | ||
208 | run_shell_cmd("find '%s' -type f -name *.pyc -exec rm {} \\;" % testdir) | ||
209 | |||
210 | no_failures = execute_tests(testdir, options.singletest) | ||
211 | |||
212 | except ShellCmdException as exc: | ||
213 | import traceback | ||
214 | config.logger.error("Error while setting up testing. Traceback: \n%s", traceback.format_exc()) | ||
215 | finally: | ||
216 | if need_cleanup and testdir is not None: | ||
217 | clean_up(testdir) | ||
218 | |||
219 | sys.exit(no_failures) | ||
220 | |||
221 | if __name__ == "__main__": | ||
222 | main() | ||