diff options
Diffstat (limited to 'bitbake/lib/toaster/contrib/tts/runner.py')
| -rwxr-xr-x | bitbake/lib/toaster/contrib/tts/runner.py | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/bitbake/lib/toaster/contrib/tts/runner.py b/bitbake/lib/toaster/contrib/tts/runner.py new file mode 100755 index 0000000000..d2a6099bdb --- /dev/null +++ b/bitbake/lib/toaster/contrib/tts/runner.py | |||
| @@ -0,0 +1,200 @@ | |||
| 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 optparse | ||
| 32 | import sys, os | ||
| 33 | import unittest, inspect, importlib | ||
| 34 | import logging, pprint, json | ||
| 35 | |||
| 36 | from shellutils import * | ||
| 37 | |||
| 38 | import config | ||
| 39 | |||
| 40 | # we also log to a file, in addition to console, because our output is important | ||
| 41 | __log_file_name =os.path.join(os.path.dirname(__file__),"log/tts_%d.log" % config.OWN_PID) | ||
| 42 | mkdirhier(os.path.dirname(__log_file_name)) | ||
| 43 | __log_file = open(__log_file_name, "w") | ||
| 44 | __file_handler = logging.StreamHandler(__log_file) | ||
| 45 | __file_handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s: %(message)s")) | ||
| 46 | |||
| 47 | config.logger.addHandler(__file_handler) | ||
| 48 | |||
| 49 | |||
| 50 | # set up log directory | ||
| 51 | try: | ||
| 52 | if not os.path.exists(config.LOGDIR): | ||
| 53 | os.mkdir(config.LOGDIR) | ||
| 54 | else: | ||
| 55 | if not os.path.isdir(config.LOGDIR): | ||
| 56 | raise Exception("Expected log dir '%s' is not actually a directory." % config.LOGDIR) | ||
| 57 | except OSError as e: | ||
| 58 | raise e | ||
| 59 | |||
| 60 | # creates the under-test-branch as a separate directory | ||
| 61 | def set_up_test_branch(settings, branch_name): | ||
| 62 | testdir = "%s/%s.%d" % (settings['workdir'], config.TEST_DIR_NAME, config.OWN_PID) | ||
| 63 | |||
| 64 | # creates the host dir | ||
| 65 | if os.path.exists(testdir): | ||
| 66 | raise Exception("Test dir '%s'is already there, aborting" % testdir) | ||
| 67 | os.mkdir(testdir) | ||
| 68 | |||
| 69 | # copies over the .git from the localclone | ||
| 70 | run_shell_cmd("cp -a '%s'/.git '%s'" % (settings['localclone'], testdir)) | ||
| 71 | |||
| 72 | # add the remote if it doesn't exist | ||
| 73 | crt_remotes = run_shell_cmd("git remote -v", cwd = testdir) | ||
| 74 | remotes = [word for line in crt_remotes.split("\n") for word in line.split()] | ||
| 75 | if not config.CONTRIB_REPO in remotes: | ||
| 76 | remote_name = "tts_contrib" | ||
| 77 | run_shell_cmd("git remote add %s %s" % (remote_name, config.CONTRIB_REPO), cwd = testdir) | ||
| 78 | else: | ||
| 79 | remote_name = remotes[remotes.index(config.CONTRIB_REPO) - 1] | ||
| 80 | |||
| 81 | # do the fetch | ||
| 82 | run_shell_cmd("git fetch %s -p" % remote_name, cwd=testdir) | ||
| 83 | |||
| 84 | # do the checkout | ||
| 85 | 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) | ||
| 86 | |||
| 87 | return testdir | ||
| 88 | |||
| 89 | |||
| 90 | def __search_for_tests(): | ||
| 91 | # we find all classes that can run, and run them | ||
| 92 | tests = [] | ||
| 93 | for dir_name, dirs_list, files_list in os.walk(os.path.dirname(os.path.abspath(__file__))): | ||
| 94 | for f in [f[:-3] for f in files_list if f.endswith(".py") and not f.startswith("__init__")]: | ||
| 95 | config.logger.debug("Inspecting module %s", f) | ||
| 96 | current_module = importlib.import_module(f) | ||
| 97 | crtclass_names = vars(current_module) | ||
| 98 | for v in crtclass_names: | ||
| 99 | t = crtclass_names[v] | ||
| 100 | if isinstance(t, type(unittest.TestCase)) and issubclass(t, unittest.TestCase): | ||
| 101 | tests.append((f,v)) | ||
| 102 | break | ||
| 103 | return tests | ||
| 104 | |||
| 105 | |||
| 106 | # boilerplate to self discover tests and run them | ||
| 107 | def execute_tests(dir_under_test, testname): | ||
| 108 | |||
| 109 | if testname is not None and "." in testname: | ||
| 110 | tests = [] | ||
| 111 | tests.append(tuple(testname.split(".", 2))) | ||
| 112 | else: | ||
| 113 | tests = __search_for_tests() | ||
| 114 | |||
| 115 | # let's move to the directory under test | ||
| 116 | crt_dir = os.getcwd() | ||
| 117 | os.chdir(dir_under_test) | ||
| 118 | |||
| 119 | # execute each module | ||
| 120 | try: | ||
| 121 | config.logger.debug("Discovered test clases: %s" % pprint.pformat(tests)) | ||
| 122 | suite = unittest.TestSuite() | ||
| 123 | loader = unittest.TestLoader() | ||
| 124 | result = unittest.TestResult() | ||
| 125 | for m,t in tests: | ||
| 126 | suite.addTest(loader.loadTestsFromName("%s.%s" % (m,t))) | ||
| 127 | config.logger.info("Running %d test(s)", suite.countTestCases()) | ||
| 128 | suite.run(result) | ||
| 129 | |||
| 130 | if len(result.errors) > 0: | ||
| 131 | map(lambda x: config.logger.error("Exception on test: %s" % pprint.pformat(x)), result.errors) | ||
| 132 | |||
| 133 | if len(result.failures) > 0: | ||
| 134 | map(lambda x: config.logger.error("Failed test: %s:\n%s\n" % (pprint.pformat(x[0]), "\n".join(["-- %s" % x for x in eval(pprint.pformat(x[1])).split("\n")]))), result.failures) | ||
| 135 | |||
| 136 | config.logger.info("Test results: %d ran, %d errors, %d failures" % (result.testsRun, len(result.errors), len(result.failures))) | ||
| 137 | |||
| 138 | except Exception as e: | ||
| 139 | import traceback | ||
| 140 | config.logger.error("Exception while running test. Tracedump: \n%s", traceback.format_exc(e)) | ||
| 141 | finally: | ||
| 142 | os.chdir(crt_dir) | ||
| 143 | return len(result.failures) | ||
| 144 | |||
| 145 | # verify that we had a branch-under-test name as parameter | ||
| 146 | def validate_args(): | ||
| 147 | from optparse import OptionParser | ||
| 148 | parser = OptionParser(usage="usage: %prog [options] branch_under_test") | ||
| 149 | |||
| 150 | parser.add_option("-t", "--test-dir", dest="testdir", default=None, help="Use specified directory to run tests, inhibits the checkout.") | ||
| 151 | parser.add_option("-s", "--single", dest="singletest", default=None, help="Run only the specified test") | ||
| 152 | |||
| 153 | (options, args) = parser.parse_args() | ||
| 154 | if len(args) < 1: | ||
| 155 | raise Exception("Please specify the branch to run on. Use option '-h' when in doubt.") | ||
| 156 | return (options, args) | ||
| 157 | |||
| 158 | |||
| 159 | |||
| 160 | |||
| 161 | # load the configuration options | ||
| 162 | def read_settings(): | ||
| 163 | if not os.path.exists(config.SETTINGS_FILE) or not os.path.isfile(config.SETTINGS_FILE): | ||
| 164 | raise Exception("Config file '%s' cannot be openend" % config.SETTINGS_FILE); | ||
| 165 | return json.loads(open(config.SETTINGS_FILE, "r").read()) | ||
| 166 | |||
| 167 | |||
| 168 | # cleanup ! | ||
| 169 | def clean_up(testdir): | ||
| 170 | # TODO: delete the test dir | ||
| 171 | #run_shell_cmd("rm -rf -- '%s'" % testdir) | ||
| 172 | pass | ||
| 173 | |||
| 174 | if __name__ == "__main__": | ||
| 175 | (options, args) = validate_args() | ||
| 176 | |||
| 177 | settings = read_settings() | ||
| 178 | need_cleanup = False | ||
| 179 | |||
| 180 | testdir = None | ||
| 181 | no_failures = 1 | ||
| 182 | try: | ||
| 183 | if options.testdir is not None and os.path.exists(options.testdir): | ||
| 184 | testdir = options.testdir | ||
| 185 | config.logger.info("No checkout, using %s" % testdir) | ||
| 186 | else: | ||
| 187 | need_cleanup = True | ||
| 188 | testdir = set_up_test_branch(settings, args[0]) # we expect a branch name as first argument | ||
| 189 | |||
| 190 | config.testdir = testdir # we let tests know where to run | ||
| 191 | no_failures = execute_tests(testdir, options.singletest) | ||
| 192 | |||
| 193 | except ShellCmdException as e : | ||
| 194 | import traceback | ||
| 195 | config.logger.error("Error while setting up testing. Traceback: \n%s" % traceback.format_exc(e)) | ||
| 196 | finally: | ||
| 197 | if need_cleanup and testdir is not None: | ||
| 198 | clean_up(testdir) | ||
| 199 | |||
| 200 | sys.exit(no_failures) | ||
