test.py revision 4beca02ac922291c63cc65a96b43eed4e26f469e
13f00e317064560ad11168d22030416d853829f6eJim Grosbach# Shell class for a test, inherited by all individual tests 23f00e317064560ad11168d22030416d853829f6eJim Grosbach# 33f00e317064560ad11168d22030416d853829f6eJim Grosbach# Methods: 4e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach# __init__ initialise 5e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach# initialize run once for each job 6e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach# setup run once for each new version of the test installed 7e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach# run run the test (wrapped by job.run_test()) 8e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach# 9e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach# Data: 10e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach# job backreference to the job this test instance is part of 113f00e317064560ad11168d22030416d853829f6eJim Grosbach# outputdir eg. results/<job>/<testname.tag> 123f00e317064560ad11168d22030416d853829f6eJim Grosbach# resultsdir eg. results/<job>/<testname.tag>/results 133f00e317064560ad11168d22030416d853829f6eJim Grosbach# profdir eg. results/<job>/<testname.tag>/profiling 14da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach# debugdir eg. results/<job>/<testname.tag>/debug 153f00e317064560ad11168d22030416d853829f6eJim Grosbach# bindir eg. tests/<test> 16da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach# src eg. tests/<test>/src 173f00e317064560ad11168d22030416d853829f6eJim Grosbach# tmpdir eg. tmp/<tempname>_<testname.tag> 183f00e317064560ad11168d22030416d853829f6eJim Grosbach 193f00e317064560ad11168d22030416d853829f6eJim Grosbach#pylint: disable-msg=C0111 203f00e317064560ad11168d22030416d853829f6eJim Grosbach 213f00e317064560ad11168d22030416d853829f6eJim Grosbachimport fcntl, json, os, re, sys, shutil, stat, tempfile, time, traceback 223f00e317064560ad11168d22030416d853829f6eJim Grosbachimport logging 233f00e317064560ad11168d22030416d853829f6eJim Grosbach 243f00e317064560ad11168d22030416d853829f6eJim Grosbachfrom autotest_lib.client.bin import utils 253f00e317064560ad11168d22030416d853829f6eJim Grosbachfrom autotest_lib.client.common_lib import error 263f00e317064560ad11168d22030416d853829f6eJim Grosbach 273f00e317064560ad11168d22030416d853829f6eJim Grosbach 28dc89561fecf100d6c32d73c7b009fd73e51be688Jim Grosbachclass base_test(object): 293f00e317064560ad11168d22030416d853829f6eJim Grosbach preserve_srcdir = False 303f00e317064560ad11168d22030416d853829f6eJim Grosbach network_destabilizing = False 313f00e317064560ad11168d22030416d853829f6eJim Grosbach 323f00e317064560ad11168d22030416d853829f6eJim Grosbach def __init__(self, job, bindir, outputdir): 333f00e317064560ad11168d22030416d853829f6eJim Grosbach self.job = job 343f00e317064560ad11168d22030416d853829f6eJim Grosbach self.pkgmgr = job.pkgmgr 353f00e317064560ad11168d22030416d853829f6eJim Grosbach self.autodir = job.autodir 363f00e317064560ad11168d22030416d853829f6eJim Grosbach self.outputdir = outputdir 373f00e317064560ad11168d22030416d853829f6eJim Grosbach self.tagged_testname = os.path.basename(self.outputdir) 383f00e317064560ad11168d22030416d853829f6eJim Grosbach self.resultsdir = os.path.join(self.outputdir, 'results') 393f00e317064560ad11168d22030416d853829f6eJim Grosbach os.mkdir(self.resultsdir) 403f00e317064560ad11168d22030416d853829f6eJim Grosbach self.profdir = os.path.join(self.outputdir, 'profiling') 413f00e317064560ad11168d22030416d853829f6eJim Grosbach os.mkdir(self.profdir) 42dc89561fecf100d6c32d73c7b009fd73e51be688Jim Grosbach self.debugdir = os.path.join(self.outputdir, 'debug') 43e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach os.mkdir(self.debugdir) 44da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach # TODO(ericli): figure out how autotest crash handler work with cros 45e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach # Once this is re-enabled import getpass. crosbug.com/31232 46da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach # crash handler, we should restore it in near term. 47da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach # if getpass.getuser() == 'root': 48e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach # self.configure_crash_handler() 49e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach # else: 50e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach self.crash_handling_enabled = False 51e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach self.bindir = bindir 52e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach self.srcdir = os.path.join(self.bindir, 'src') 53e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach self.tmpdir = tempfile.mkdtemp("_" + self.tagged_testname, 54e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach dir=job.tmpdir) 55e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach self._keyvals = [] 56e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach self._new_keyval = False 57e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach self.failed_constraints = [] 58e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach self.iteration = 0 59e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach self.before_iteration_hooks = [] 60e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach self.after_iteration_hooks = [] 61e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach 62e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach # Flag to indicate if the test has succeeded or failed. 63e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach self.success = False 64e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach 65e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach 66e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach def configure_crash_handler(self): 67e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach pass 6837ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach 6937ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach 7037ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach def crash_handler_report(self): 7137ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach pass 7237ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach 7337ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach 7437ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach def assert_(self, expr, msg='Assertion failed.'): 7537ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach if not expr: 7637ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach raise error.TestError(msg) 7737ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach 7837ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach 7937ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach def write_test_keyval(self, attr_dict): 8037ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach utils.write_keyval(self.outputdir, attr_dict, 8137ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach tap_report=self.job._tap) 8237ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach 8337ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach 8437ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach @staticmethod 8537ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach def _append_type_to_keys(dictionary, typename): 8637ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach new_dict = {} 87e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach for key, value in dictionary.iteritems(): 88e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach new_key = "%s{%s}" % (key, typename) 89e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach new_dict[new_key] = value 90e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach return new_dict 91e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach 92e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach 93e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach def output_perf_value(self, description, value, units=None, 94e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach higher_is_better=None, graph=None, replacement='_'): 95e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach """ 96e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach Records a measured performance value in an output file. 97e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach 98e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach The output file will subsequently be parsed by the TKO parser to have 99e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach the information inserted into the results database. 100e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach 101e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach @param description: A string describing the measured perf value. Must 102e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach be maximum length 256, and may only contain letters, numbers, 103e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach periods, dashes, and underscores. For example: 104e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach "page_load_time", "scrolling-frame-rate". 105e8606dc7c878d4562da5e3e5609b9d7d734d498cJim Grosbach @param value: A number representing the measured perf value, or a list 10637ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach of measured values if a test takes multiple measurements. 10737ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach Measured perf values can be either ints or floats. 10837ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach @param units: A string describing the units associated with the 10937ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach measured perf value. Must be maximum length 32, and may only 11037ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach contain letters, numbers, periods, dashes, and underscores. 11137ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach For example: "msec", "fps", "score", "runs_per_second". 11237ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach @param higher_is_better: A boolean indicating whether or not a "higher" 11337ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach measured perf value is considered to be better. If False, it is 11437ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach assumed that a "lower" measured value is considered to be 11537ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach better. This impacts dashboard plotting and email notification. 11637ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach Pure autotests are expected to specify either True or False! 11737ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach This value can be set to "None" to indicate that the perf 11837ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach dashboard should apply the rules encoded via Chromium 11937ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach unit-info.json. This is only used for tracking Chromium based 12037ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach tests (in particular telemetry). 12137ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach @param graph: A string indicating the name of the graph on which 12237ee464ea98544d3ed84cec6dde5f769ce003d5fJim Grosbach the perf value will be subsequently displayed on the chrome perf 123da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach dashboard. This allows multiple metrics be grouped together on 124da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach the same graphs. Defaults to None, indicating that the perf 125da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach value should be displayed individually on a separate graph. 1267ed6d22e9637c52b3511ac6907830251d1124e60Jim Grosbach @param replacement: string to replace illegal characters in 1277ed6d22e9637c52b3511ac6907830251d1124e60Jim Grosbach |description| and |units| with. 1287ed6d22e9637c52b3511ac6907830251d1124e60Jim Grosbach """ 1297ed6d22e9637c52b3511ac6907830251d1124e60Jim Grosbach if len(description) > 256: 130da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach raise ValueError('The description must be at most 256 characters.') 131da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach if len(units) > 32: 132da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach raise ValueError('The units must be at most 32 characters.') 133da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 134da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach # If |replacement| is legal replace illegal characters with it. 135da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach string_regex = re.compile(r'[^-\.\w]') 136da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach if replacement is None or re.search(string_regex, replacement): 137da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach raise ValueError('Invalid replacement string to mask illegal ' 138da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 'characters. May only contain letters, numbers, ' 139da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 'periods, dashes, and underscores. ' 140da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 'replacement: %s' % replacement) 141da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach description = re.sub(string_regex, replacement, description) 142da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach units = re.sub(string_regex, replacement, units) if units else None 143da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 144da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach entry = { 145da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 'description': description, 146da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 'value': value, 147da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 'units': units, 148da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 'higher_is_better': higher_is_better, 149da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 'graph': graph 150da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach } 151da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 152da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach output_path = os.path.join(self.resultsdir, 'perf_measurements') 153da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach with open(output_path, 'a') as fp: 154da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach fp.write(json.dumps(entry, sort_keys=True) + '\n') 155da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 156da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 157da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach def write_perf_keyval(self, perf_dict): 158da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach self.write_iteration_keyval({}, perf_dict, 159da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach tap_report=self.job._tap) 160da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 161da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 162da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach def write_attr_keyval(self, attr_dict): 163da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach self.write_iteration_keyval(attr_dict, {}, 164da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach tap_report=self.job._tap) 165da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 166da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 167da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach def write_iteration_keyval(self, attr_dict, perf_dict, tap_report=None): 168da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach # append the dictionaries before they have the {perf} and {attr} added 169da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach self._keyvals.append({'attr':attr_dict, 'perf':perf_dict}) 170da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach self._new_keyval = True 171da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 172da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach if attr_dict: 173da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach attr_dict = self._append_type_to_keys(attr_dict, "attr") 174da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach utils.write_keyval(self.resultsdir, attr_dict, type_tag="attr", 175da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach tap_report=tap_report) 176da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 177da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach if perf_dict: 178da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach perf_dict = self._append_type_to_keys(perf_dict, "perf") 179da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach utils.write_keyval(self.resultsdir, perf_dict, type_tag="perf", 180da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach tap_report=tap_report) 181da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 182da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach keyval_path = os.path.join(self.resultsdir, "keyval") 183da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach print >> open(keyval_path, "a"), "" 184da9f278c741e8ced7c1652720270918eb04ed348Jim Grosbach 18559642c260064a0c9140e048d702a21830020487fJim Grosbach 18659642c260064a0c9140e048d702a21830020487fJim Grosbach def analyze_perf_constraints(self, constraints): 18759642c260064a0c9140e048d702a21830020487fJim Grosbach if not self._new_keyval: 18859642c260064a0c9140e048d702a21830020487fJim Grosbach return 18959642c260064a0c9140e048d702a21830020487fJim Grosbach 19059642c260064a0c9140e048d702a21830020487fJim Grosbach # create a dict from the keyvals suitable as an environment for eval 19159642c260064a0c9140e048d702a21830020487fJim Grosbach keyval_env = self._keyvals[-1]['perf'].copy() 19259642c260064a0c9140e048d702a21830020487fJim Grosbach keyval_env['__builtins__'] = None 19359642c260064a0c9140e048d702a21830020487fJim Grosbach self._new_keyval = False 19459642c260064a0c9140e048d702a21830020487fJim Grosbach failures = [] 19559642c260064a0c9140e048d702a21830020487fJim Grosbach 19659642c260064a0c9140e048d702a21830020487fJim Grosbach # evaluate each constraint using the current keyvals 19759642c260064a0c9140e048d702a21830020487fJim Grosbach for constraint in constraints: 19859642c260064a0c9140e048d702a21830020487fJim Grosbach logging.info('___________________ constraint = %s', constraint) 19959642c260064a0c9140e048d702a21830020487fJim Grosbach logging.info('___________________ keyvals = %s', keyval_env) 20059642c260064a0c9140e048d702a21830020487fJim Grosbach 20159642c260064a0c9140e048d702a21830020487fJim Grosbach try: 20259642c260064a0c9140e048d702a21830020487fJim Grosbach if not eval(constraint, keyval_env): 20359642c260064a0c9140e048d702a21830020487fJim Grosbach failures.append('%s: constraint was not met' % constraint) 20459642c260064a0c9140e048d702a21830020487fJim Grosbach except: 20559642c260064a0c9140e048d702a21830020487fJim Grosbach failures.append('could not evaluate constraint: %s' 20659642c260064a0c9140e048d702a21830020487fJim Grosbach % constraint) 20759642c260064a0c9140e048d702a21830020487fJim Grosbach 20859642c260064a0c9140e048d702a21830020487fJim Grosbach # keep track of the errors for each iteration 20959642c260064a0c9140e048d702a21830020487fJim Grosbach self.failed_constraints.append(failures) 21059642c260064a0c9140e048d702a21830020487fJim Grosbach 21159642c260064a0c9140e048d702a21830020487fJim Grosbach 21259642c260064a0c9140e048d702a21830020487fJim Grosbach def process_failed_constraints(self): 21359642c260064a0c9140e048d702a21830020487fJim Grosbach msg = '' 21459642c260064a0c9140e048d702a21830020487fJim Grosbach for i, failures in enumerate(self.failed_constraints): 21559642c260064a0c9140e048d702a21830020487fJim Grosbach if failures: 21659642c260064a0c9140e048d702a21830020487fJim Grosbach msg += 'iteration %d:%s ' % (i, ','.join(failures)) 21759642c260064a0c9140e048d702a21830020487fJim Grosbach 21859642c260064a0c9140e048d702a21830020487fJim Grosbach if msg: 21959642c260064a0c9140e048d702a21830020487fJim Grosbach raise error.TestFail(msg) 22059642c260064a0c9140e048d702a21830020487fJim Grosbach 22159642c260064a0c9140e048d702a21830020487fJim Grosbach 22259642c260064a0c9140e048d702a21830020487fJim Grosbach def register_before_iteration_hook(self, iteration_hook): 22359642c260064a0c9140e048d702a21830020487fJim Grosbach """ 22459642c260064a0c9140e048d702a21830020487fJim Grosbach This is how we expect test writers to register a before_iteration_hook. 22559642c260064a0c9140e048d702a21830020487fJim Grosbach This adds the method to the list of hooks which are executed 22659642c260064a0c9140e048d702a21830020487fJim Grosbach before each iteration. 22759642c260064a0c9140e048d702a21830020487fJim Grosbach 22859642c260064a0c9140e048d702a21830020487fJim Grosbach @param iteration_hook: Method to run before each iteration. A valid 22959642c260064a0c9140e048d702a21830020487fJim Grosbach hook accepts a single argument which is the 23059642c260064a0c9140e048d702a21830020487fJim Grosbach test object. 23159642c260064a0c9140e048d702a21830020487fJim Grosbach """ 23259642c260064a0c9140e048d702a21830020487fJim Grosbach self.before_iteration_hooks.append(iteration_hook) 23359642c260064a0c9140e048d702a21830020487fJim Grosbach 23459642c260064a0c9140e048d702a21830020487fJim Grosbach 23559642c260064a0c9140e048d702a21830020487fJim Grosbach def register_after_iteration_hook(self, iteration_hook): 23659642c260064a0c9140e048d702a21830020487fJim Grosbach """ 23759642c260064a0c9140e048d702a21830020487fJim Grosbach This is how we expect test writers to register an after_iteration_hook. 23859642c260064a0c9140e048d702a21830020487fJim Grosbach This adds the method to the list of hooks which are executed 23959642c260064a0c9140e048d702a21830020487fJim Grosbach after each iteration. Hooks are executed starting with the most- 24059642c260064a0c9140e048d702a21830020487fJim Grosbach recently registered, in stack fashion. 24159642c260064a0c9140e048d702a21830020487fJim Grosbach 24259642c260064a0c9140e048d702a21830020487fJim Grosbach @param iteration_hook: Method to run after each iteration. A valid 2437ed6d22e9637c52b3511ac6907830251d1124e60Jim Grosbach hook accepts a single argument which is the 2447ed6d22e9637c52b3511ac6907830251d1124e60Jim Grosbach test object. 2457ed6d22e9637c52b3511ac6907830251d1124e60Jim Grosbach """ 2467ed6d22e9637c52b3511ac6907830251d1124e60Jim Grosbach self.after_iteration_hooks.append(iteration_hook) 2477ed6d22e9637c52b3511ac6907830251d1124e60Jim Grosbach 2487ed6d22e9637c52b3511ac6907830251d1124e60Jim Grosbach 2497ed6d22e9637c52b3511ac6907830251d1124e60Jim Grosbach def initialize(self): 2507ed6d22e9637c52b3511ac6907830251d1124e60Jim Grosbach pass 2517ed6d22e9637c52b3511ac6907830251d1124e60Jim Grosbach 2527ed6d22e9637c52b3511ac6907830251d1124e60Jim Grosbach 2537ed6d22e9637c52b3511ac6907830251d1124e60Jim Grosbach def setup(self): 2547ed6d22e9637c52b3511ac6907830251d1124e60Jim Grosbach pass 255e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach 256e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach 25776cbe02cdd57a297d9c6f1e5106e4718abd7ff9fJim Grosbach def warmup(self, *args, **dargs): 258e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach pass 259e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach 260e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach 261e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach def drop_caches_between_iterations(self): 262e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach if self.job.drop_caches_between_iterations: 263e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach utils.drop_caches() 264e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach 265e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach 266e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach def _call_run_once_with_retry(self, constraints, profile_only, 267e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach postprocess_profiled_run, args, dargs): 268e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach """Thin wrapper around _call_run_once that retries unsuccessful tests. 269e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach 270e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach If the job object's attribute test_retry is > 0 retry any tests that 271e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach ran unsuccessfully X times. 272e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach *Note this does not competely re-initialize the test, it only 273e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach re-executes code once all the initial job set up (packages, 274e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach sysinfo, etc) is complete. 275e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach """ 276e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach if self.job.test_retry != 0: 277e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach logging.info('Test will be retried a maximum of %d times', 278e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach self.job.test_retry) 279e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach 280e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach max_runs = self.job.test_retry 281e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach for retry_run in xrange(0, max_runs+1): 282e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach try: 283e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach self._call_run_once(constraints, profile_only, 284e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach postprocess_profiled_run, args, dargs) 285e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach break 286e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach except error.TestFailRetry as err: 287e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach if retry_run == max_runs: 288e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach raise 289e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach self.job.record('INFO', None, None, 'Run %s failed with %s' % ( 290e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach retry_run, err)) 291e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach if retry_run > 0: 292e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach self.write_test_keyval({'test_retries_before_success': retry_run}) 293e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach 294e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach 295e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach def _call_run_once(self, constraints, profile_only, 296e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach postprocess_profiled_run, args, dargs): 297e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach self.drop_caches_between_iterations() 298e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach # execute iteration hooks 299e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach for hook in self.before_iteration_hooks: 300e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach hook(self) 301e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach 302e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach try: 303e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach if profile_only: 304e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach if not self.job.profilers.present(): 305e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach self.job.record('WARN', None, None, 306e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach 'No profilers have been added but ' 307e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach 'profile_only is set - nothing ' 308e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach 'will be run') 309e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach self.run_once_profiling(postprocess_profiled_run, 310e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach *args, **dargs) 311e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach else: 312e52240c3705f3133eb8c4ebb4220054c68de2651Jim Grosbach self.before_run_once() 31321101d60ce94f51651f71eeb61ceb8264eccac83Jim Grosbach self.run_once(*args, **dargs) 31421101d60ce94f51651f71eeb61ceb8264eccac83Jim Grosbach self.after_run_once() 31521101d60ce94f51651f71eeb61ceb8264eccac83Jim Grosbach 31621101d60ce94f51651f71eeb61ceb8264eccac83Jim Grosbach self.postprocess_iteration() 31721101d60ce94f51651f71eeb61ceb8264eccac83Jim Grosbach self.analyze_perf_constraints(constraints) 31821101d60ce94f51651f71eeb61ceb8264eccac83Jim Grosbach # Catch and re-raise to let after_iteration_hooks see the exception. 319fff76ee7ef007b2bb74804f165fee475e30ead0dJim Grosbach except: 320fff76ee7ef007b2bb74804f165fee475e30ead0dJim Grosbach raise 32137023b05c84000373fcfc0871edad3c2b995be33Jim Grosbach finally: 32237023b05c84000373fcfc0871edad3c2b995be33Jim Grosbach for hook in reversed(self.after_iteration_hooks): 32337023b05c84000373fcfc0871edad3c2b995be33Jim Grosbach hook(self) 32437023b05c84000373fcfc0871edad3c2b995be33Jim Grosbach 32537023b05c84000373fcfc0871edad3c2b995be33Jim Grosbach 32637023b05c84000373fcfc0871edad3c2b995be33Jim Grosbach def execute(self, iterations=None, test_length=None, profile_only=None, 32737023b05c84000373fcfc0871edad3c2b995be33Jim Grosbach _get_time=time.time, postprocess_profiled_run=None, 32837023b05c84000373fcfc0871edad3c2b995be33Jim Grosbach constraints=(), *args, **dargs): 329f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach """ 330f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach This is the basic execute method for the tests inherited from base_test. 331f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach If you want to implement a benchmark test, it's better to implement 332f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach the run_once function, to cope with the profiling infrastructure. For 333f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach other tests, you can just override the default implementation. 334f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach 33537023b05c84000373fcfc0871edad3c2b995be33Jim Grosbach @param test_length: The minimum test length in seconds. We'll run the 33637023b05c84000373fcfc0871edad3c2b995be33Jim Grosbach run_once function for a number of times large enough to cover the 33737023b05c84000373fcfc0871edad3c2b995be33Jim Grosbach minimum test length. 33837023b05c84000373fcfc0871edad3c2b995be33Jim Grosbach 33937023b05c84000373fcfc0871edad3c2b995be33Jim Grosbach @param iterations: A number of iterations that we'll run the run_once 34037023b05c84000373fcfc0871edad3c2b995be33Jim Grosbach function. This parameter is incompatible with test_length and will 341f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach be silently ignored if you specify both. 342f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach 343f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach @param profile_only: If true run X iterations with profilers enabled. 344f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach If false run X iterations and one with profiling if profiles are 345f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach enabled. If None, default to the value of job.default_profile_only. 346f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach 347f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach @param _get_time: [time.time] Used for unit test time injection. 348f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach 349f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach @param postprocess_profiled_run: Run the postprocessing for the 350f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach profiled run. 351f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach """ 352f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach 353f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach # For our special class of tests, the benchmarks, we don't want 354f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach # profilers to run during the test iterations. Let's reserve only 355f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach # the last iteration for profiling, if needed. So let's stop 356f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach # all profilers if they are present and active. 35737023b05c84000373fcfc0871edad3c2b995be33Jim Grosbach profilers = self.job.profilers 358f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach if profilers.active(): 359f333d471d2cdd47d830dfe3a3e40efbb106c100dJim Grosbach profilers.stop(self) 36083ab070fc1fbb02ca77b0a37e6ae0eacf58001e1Jim Grosbach if profile_only is None: 36183ab070fc1fbb02ca77b0a37e6ae0eacf58001e1Jim Grosbach profile_only = self.job.default_profile_only 36283ab070fc1fbb02ca77b0a37e6ae0eacf58001e1Jim Grosbach # If the user called this test in an odd way (specified both iterations 36383ab070fc1fbb02ca77b0a37e6ae0eacf58001e1Jim Grosbach # and test_length), let's warn them. 36483ab070fc1fbb02ca77b0a37e6ae0eacf58001e1Jim Grosbach if iterations and test_length: 36583ab070fc1fbb02ca77b0a37e6ae0eacf58001e1Jim Grosbach logging.debug('Iterations parameter ignored (timed execution)') 36683ab070fc1fbb02ca77b0a37e6ae0eacf58001e1Jim Grosbach if test_length: 36783ab070fc1fbb02ca77b0a37e6ae0eacf58001e1Jim Grosbach test_start = _get_time() 36883ab070fc1fbb02ca77b0a37e6ae0eacf58001e1Jim Grosbach time_elapsed = 0 369d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach timed_counter = 0 370d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach logging.debug('Test started. Specified %d s as the minimum test ' 37183ab070fc1fbb02ca77b0a37e6ae0eacf58001e1Jim Grosbach 'length', test_length) 372d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach while time_elapsed < test_length: 373d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach timed_counter = timed_counter + 1 374d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach if time_elapsed == 0: 375d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach logging.debug('Executing iteration %d', timed_counter) 376d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach elif time_elapsed > 0: 377d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach logging.debug('Executing iteration %d, time_elapsed %d s', 378d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach timed_counter, time_elapsed) 379d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach self._call_run_once_with_retry(constraints, profile_only, 380d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach postprocess_profiled_run, args, 381d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach dargs) 382d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach test_iteration_finish = _get_time() 383d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach time_elapsed = test_iteration_finish - test_start 384d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach logging.debug('Test finished after %d iterations, ' 385d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach 'time elapsed: %d s', timed_counter, time_elapsed) 386d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach else: 387d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach if iterations is None: 388d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach iterations = 1 389d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach if iterations > 1: 390d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach logging.debug('Test started. Specified %d iterations', 391d986bc66bc56251c2b7d5b9a89df14c4760568fcJim Grosbach iterations) 39214ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach for self.iteration in xrange(1, iterations + 1): 39314ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach if iterations > 1: 39414ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach logging.debug('Executing iteration %d of %d', 39514ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach self.iteration, iterations) 39614ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach self._call_run_once_with_retry(constraints, profile_only, 39714ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach postprocess_profiled_run, args, 39814ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach dargs) 39914ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 40014ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach if not profile_only: 40114ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach self.iteration += 1 40214ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach self.run_once_profiling(postprocess_profiled_run, *args, **dargs) 40314ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 40414ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach # Do any postprocessing, normally extracting performance keyvals, etc 40514ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach self.postprocess() 40614ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach self.process_failed_constraints() 40714ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 40814ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 40914ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach def run_once_profiling(self, postprocess_profiled_run, *args, **dargs): 41014ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach profilers = self.job.profilers 41114ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach # Do a profiling run if necessary 41214ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach if profilers.present(): 41314ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach self.drop_caches_between_iterations() 41414ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach profilers.before_start(self) 41514ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 41614ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach self.before_run_once() 41714ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach profilers.start(self) 41814ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach logging.debug('Profilers present. Profiling run started') 41914ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 42014ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach try: 42114ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach self.run_once(*args, **dargs) 42214ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 42314ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach # Priority to the run_once() argument over the attribute. 42414ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach postprocess_attribute = getattr(self, 42514ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 'postprocess_profiled_run', 42614ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach False) 42714ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 42814ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach if (postprocess_profiled_run or 42914ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach (postprocess_profiled_run is None and 43014ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach postprocess_attribute)): 43114ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach self.postprocess_iteration() 43214ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 43314ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach finally: 43414ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach profilers.stop(self) 43514ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach profilers.report(self) 43614ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 43714ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach self.after_run_once() 43814ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 43914ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 44014ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach def postprocess(self): 44114ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach pass 44214ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 44314ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 44414ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach def postprocess_iteration(self): 44514ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach pass 44614ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 44714ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 44814ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach def cleanup(self): 44914ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach pass 45014ab1c3387a240a914cf8b1907bb3609bae72269Jim Grosbach 4516f9f8845028d4d3b96c33417398034a71137d867Jim Grosbach 4526f9f8845028d4d3b96c33417398034a71137d867Jim Grosbach def before_run_once(self): 4536f9f8845028d4d3b96c33417398034a71137d867Jim Grosbach """ 4546f9f8845028d4d3b96c33417398034a71137d867Jim Grosbach Override in tests that need it, will be called before any run_once() 4556f9f8845028d4d3b96c33417398034a71137d867Jim Grosbach call including the profiling run (when it's called before starting 4566f9f8845028d4d3b96c33417398034a71137d867Jim Grosbach the profilers). 4576f9f8845028d4d3b96c33417398034a71137d867Jim Grosbach """ 4586f9f8845028d4d3b96c33417398034a71137d867Jim Grosbach pass 4596f9f8845028d4d3b96c33417398034a71137d867Jim Grosbach 4606f9f8845028d4d3b96c33417398034a71137d867Jim Grosbach 461032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach def after_run_once(self): 462032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach """ 463032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach Called after every run_once (including from a profiled run when it's 464032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach called after stopping the profilers). 465032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach """ 466032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach pass 467032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach 468032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach 469032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach @staticmethod 470032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach def _make_writable_to_others(directory): 471032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach mode = os.stat(directory).st_mode 472032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach mode = mode | stat.S_IROTH | stat.S_IWOTH | stat.S_IXOTH 473032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach os.chmod(directory, mode) 474032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach 475032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach 476032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach def _exec(self, args, dargs): 477032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach self.job.logging.tee_redirect_debug_dir(self.debugdir, 478032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach log_name=self.tagged_testname) 479032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach try: 480032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach if self.network_destabilizing: 481032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach self.job.disable_warnings("NETWORK") 482032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach 483032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach # write out the test attributes into a keyval 484032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach dargs = dargs.copy() 485032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach run_cleanup = dargs.pop('run_cleanup', self.job.run_test_cleanup) 486032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach keyvals = dargs.pop('test_attributes', {}).copy() 487032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach keyvals['version'] = self.version 488032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach for i, arg in enumerate(args): 489032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach keyvals['param-%d' % i] = repr(arg) 490032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach for name, arg in dargs.iteritems(): 491032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach keyvals['param-%s' % name] = repr(arg) 492032434d622b6cd030a60bb9045a520c93b0d7d68Jim Grosbach self.write_test_keyval(keyvals) 493e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach 494e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach _validate_args(args, dargs, self.initialize, self.setup, 495e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach self.execute, self.cleanup) 496e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach 497e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach try: 498e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach # Make resultsdir and tmpdir accessible to everyone. We may 499e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach # output data to these directories as others, e.g., chronos. 500e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach self._make_writable_to_others(self.tmpdir) 501e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach self._make_writable_to_others(self.resultsdir) 502e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach 503e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach # Initialize: 504e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach _cherry_pick_call(self.initialize, *args, **dargs) 505e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach 506e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach lockfile = open(os.path.join(self.job.tmpdir, '.testlock'), 'w') 507e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach try: 508e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach fcntl.flock(lockfile, fcntl.LOCK_EX) 509e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach # Setup: (compile and install the test, if needed) 510e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach p_args, p_dargs = _cherry_pick_args(self.setup, args, dargs) 511e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach utils.update_version(self.srcdir, self.preserve_srcdir, 512e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach self.version, self.setup, 513e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach *p_args, **p_dargs) 514e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach finally: 515e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach fcntl.flock(lockfile, fcntl.LOCK_UN) 516e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach lockfile.close() 517e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach 518e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach # Execute: 519e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach os.chdir(self.outputdir) 520e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach 521e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach # call self.warmup cherry picking the arguments it accepts and 522e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach # translate exceptions if needed 523e77494e3e3da59afaa51d1bbcf732fa2851d865dJim Grosbach _call_test_function(_cherry_pick_call, self.warmup, 52400a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach *args, **dargs) 52500a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach 52600a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach if hasattr(self, 'run_once'): 52700a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach p_args, p_dargs = _cherry_pick_args(self.run_once, 52800a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach args, dargs) 52900a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach # pull in any non-* and non-** args from self.execute 53000a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach for param in _get_nonstar_args(self.execute): 53100a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach if param in dargs: 53200a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach p_dargs[param] = dargs[param] 53300a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach else: 53400a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach p_args, p_dargs = _cherry_pick_args(self.execute, 53500a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach args, dargs) 53600a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach 53700a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach _call_test_function(self.execute, *p_args, **p_dargs) 53800a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach except Exception: 53900a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach # Save the exception while we run our cleanup() before 54000a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach # reraising it, but log it to so actual time of error is known. 54100a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach exc_info = sys.exc_info() 54200a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach logging.warning('Autotest caught exception when running test:', 54300a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach exc_info=True) 54400a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach 54500a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach try: 54600a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach try: 54700a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach if run_cleanup: 54800a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach _cherry_pick_call(self.cleanup, *args, **dargs) 54900a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach except Exception: 55000a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach logging.error('Ignoring exception during cleanup() ' 55100a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach 'phase:') 55200a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach traceback.print_exc() 55300a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach logging.error('Now raising the earlier %s error', 55400a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach exc_info[0]) 55500a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach self.crash_handler_report() 55600a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach finally: 55700a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach self.job.logging.restore() 55800a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach try: 55900a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach raise exc_info[0], exc_info[1], exc_info[2] 56000a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach finally: 56100a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach # http://docs.python.org/library/sys.html#sys.exc_info 56200a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach # Be nice and prevent a circular reference. 56300a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach del exc_info 56400a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach else: 56500a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach try: 56600a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach if run_cleanup: 56700a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach _cherry_pick_call(self.cleanup, *args, **dargs) 56800a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach self.crash_handler_report() 56900a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach finally: 57000a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach self.job.logging.restore() 57100a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach except error.AutotestError: 57200a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach if self.network_destabilizing: 57300a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach self.job.enable_warnings("NETWORK") 57400a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach # Pass already-categorized errors on up. 57500a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach raise 57600a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach except Exception, e: 57700a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach if self.network_destabilizing: 57800a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach self.job.enable_warnings("NETWORK") 57900a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach # Anything else is an ERROR in our own code, not execute(). 58000a66653cbe56dfbdb831172b54097bf8256a191Jim Grosbach raise error.UnhandledTestError(e) 581791feea10071223886e2fe2bfa0e1f4cb2c0ce74Jim Grosbach else: 582791feea10071223886e2fe2bfa0e1f4cb2c0ce74Jim Grosbach if self.network_destabilizing: 583791feea10071223886e2fe2bfa0e1f4cb2c0ce74Jim Grosbach self.job.enable_warnings("NETWORK") 584791feea10071223886e2fe2bfa0e1f4cb2c0ce74Jim Grosbach 585791feea10071223886e2fe2bfa0e1f4cb2c0ce74Jim Grosbach 586791feea10071223886e2fe2bfa0e1f4cb2c0ce74Jim Grosbach def runsubtest(self, url, *args, **dargs): 587791feea10071223886e2fe2bfa0e1f4cb2c0ce74Jim Grosbach """ 588791feea10071223886e2fe2bfa0e1f4cb2c0ce74Jim Grosbach Execute another autotest test from inside the current test's scope. 589791feea10071223886e2fe2bfa0e1f4cb2c0ce74Jim Grosbach 590791feea10071223886e2fe2bfa0e1f4cb2c0ce74Jim Grosbach @param test: Parent test. 5913b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach @param url: Url of new test. 5923b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach @param tag: Tag added to test name. 5933b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach @param args: Args for subtest. 5943b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach @param dargs: Dictionary with args for subtest. 5953b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach @iterations: Number of subtest iterations. 5963b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach @profile_only: If true execute one profiled run. 5973b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach """ 5983b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach dargs["profile_only"] = dargs.get("profile_only", False) 5993b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach test_basepath = self.outputdir[len(self.job.resultdir + "/"):] 6003b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach return self.job.run_test(url, master_testpath=test_basepath, 6013b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach *args, **dargs) 6023b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach 6033b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach 6043b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbachdef _get_nonstar_args(func): 6053b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach """Extract all the (normal) function parameter names. 6063b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach 6073b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach Given a function, returns a tuple of parameter names, specifically 6083b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach excluding the * and ** parameters, if the function accepts them. 6093b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach 6103b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach @param func: A callable that we want to chose arguments for. 6113b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach 6123b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach @return: A tuple of parameters accepted by the function. 6133b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach """ 6143b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach return func.func_code.co_varnames[:func.func_code.co_argcount] 6153b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach 6163b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach 6173b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbachdef _cherry_pick_args(func, args, dargs): 6183b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach """Sanitize positional and keyword arguments before calling a function. 6193b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach 6203b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach Given a callable (func), an argument tuple and a dictionary of keyword 6213b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach arguments, pick only those arguments which the function is prepared to 622e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach accept and return a new argument tuple and keyword argument dictionary. 623e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach 624e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach Args: 625e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach func: A callable that we want to choose arguments for. 626e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach args: A tuple of positional arguments to consider passing to func. 627e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach dargs: A dictionary of keyword arguments to consider passing to func. 628e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach Returns: 629e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach A tuple of: (args tuple, keyword arguments dictionary) 630e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach """ 631e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach # Cherry pick args: 632e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach if func.func_code.co_flags & 0x04: 633e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach # func accepts *args, so return the entire args. 634e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach p_args = args 635e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach else: 636e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach p_args = () 637e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach 638e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach # Cherry pick dargs: 639e540c7422ca13c950f0e8f6f93af7225bb7742a9Jim Grosbach if func.func_code.co_flags & 0x08: 6403b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach # func accepts **dargs, so return the entire dargs. 6413b14a5c5469176effb921d91d4494f0aa2919fd0Jim Grosbach p_dargs = dargs 642c8ae39e746a20dc326def0ccfc052df3e21f16d3Jim Grosbach else: 643c8ae39e746a20dc326def0ccfc052df3e21f16d3Jim Grosbach # Only return the keyword arguments that func accepts. 644c8ae39e746a20dc326def0ccfc052df3e21f16d3Jim Grosbach p_dargs = {} 645c8ae39e746a20dc326def0ccfc052df3e21f16d3Jim Grosbach for param in _get_nonstar_args(func): 646c8ae39e746a20dc326def0ccfc052df3e21f16d3Jim Grosbach if param in dargs: 647c8ae39e746a20dc326def0ccfc052df3e21f16d3Jim Grosbach p_dargs[param] = dargs[param] 648c8ae39e746a20dc326def0ccfc052df3e21f16d3Jim Grosbach 649c8ae39e746a20dc326def0ccfc052df3e21f16d3Jim Grosbach return p_args, p_dargs 65070d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach 65170d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach 65270d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbachdef _cherry_pick_call(func, *args, **dargs): 65370d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach """Cherry picks arguments from args/dargs based on what "func" accepts 65470d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach and calls the function with the picked arguments.""" 65570d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach p_args, p_dargs = _cherry_pick_args(func, args, dargs) 65670d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach return func(*p_args, **p_dargs) 65770d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach 65870d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach 65970d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbachdef _validate_args(args, dargs, *funcs): 66070d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach """Verify that arguments are appropriate for at least one callable. 66170d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach 66270d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach Given a list of callables as additional parameters, verify that 66370d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach the proposed keyword arguments in dargs will each be accepted by at least 66470d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach one of the callables. 66570d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach 66670d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach NOTE: args is currently not supported and must be empty. 66770d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach 66870d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach Args: 66970d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach args: A tuple of proposed positional arguments. 67070d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach dargs: A dictionary of proposed keyword arguments. 67170d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach *funcs: Callables to be searched for acceptance of args and dargs. 67270d8fcfaa04eb20541b006a8fb97cbc1d3033cc4Jim Grosbach Raises: 673c8ae39e746a20dc326def0ccfc052df3e21f16d3Jim Grosbach error.AutotestError: if an arg won't be accepted by any of *funcs. 674ffa3225e26cc1977d20f0d9649fcd6f38a3c4815Jim Grosbach """ 675ffa3225e26cc1977d20f0d9649fcd6f38a3c4815Jim Grosbach all_co_flags = 0 676ffa3225e26cc1977d20f0d9649fcd6f38a3c4815Jim Grosbach all_varnames = () 677ffa3225e26cc1977d20f0d9649fcd6f38a3c4815Jim Grosbach for func in funcs: 678ffa3225e26cc1977d20f0d9649fcd6f38a3c4815Jim Grosbach all_co_flags |= func.func_code.co_flags 679ffa3225e26cc1977d20f0d9649fcd6f38a3c4815Jim Grosbach all_varnames += func.func_code.co_varnames[:func.func_code.co_argcount] 680ffa3225e26cc1977d20f0d9649fcd6f38a3c4815Jim Grosbach 6815f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach # Check if given args belongs to at least one of the methods below. 6825f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach if len(args) > 0: 6835f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach # Current implementation doesn't allow the use of args. 684ffa3225e26cc1977d20f0d9649fcd6f38a3c4815Jim Grosbach raise error.TestError('Unnamed arguments not accepted. Please ' 685ffa3225e26cc1977d20f0d9649fcd6f38a3c4815Jim Grosbach 'call job.run_test with named args only') 686ffa3225e26cc1977d20f0d9649fcd6f38a3c4815Jim Grosbach 687ffa3225e26cc1977d20f0d9649fcd6f38a3c4815Jim Grosbach # Check if given dargs belongs to at least one of the methods below. 688ffa3225e26cc1977d20f0d9649fcd6f38a3c4815Jim Grosbach if len(dargs) > 0: 689ffa3225e26cc1977d20f0d9649fcd6f38a3c4815Jim Grosbach if not all_co_flags & 0x08: 6905f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach # no func accepts *dargs, so: 6915f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach for param in dargs: 6925f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach if not param in all_varnames: 6935f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach raise error.AutotestError('Unknown parameter: %s' % param) 6945f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach 6955f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach 6965f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbachdef _installtest(job, url): 6975f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach (group, name) = job.pkgmgr.get_package_name(url, 'test') 6985f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach 6995f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach # Bail if the test is already installed 7005f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach group_dir = os.path.join(job.testdir, "download", group) 7015f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach if os.path.exists(os.path.join(group_dir, name)): 7025f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach return (group, name) 7035f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach 7045f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach # If the group directory is missing create it and add 7055f16057d1e4b711d492091bc555693a03d4a1b6eJim Grosbach # an empty __init__.py so that sub-directories are 706ffa3225e26cc1977d20f0d9649fcd6f38a3c4815Jim Grosbach # considered for import. 707ffa3225e26cc1977d20f0d9649fcd6f38a3c4815Jim Grosbach if not os.path.exists(group_dir): 7081a2be4db5b12cb7bfa351bcebd5e94b0decb021fJim Grosbach os.makedirs(group_dir) 7091a2be4db5b12cb7bfa351bcebd5e94b0decb021fJim Grosbach f = file(os.path.join(group_dir, '__init__.py'), 'w+') 7101a2be4db5b12cb7bfa351bcebd5e94b0decb021fJim Grosbach f.close() 7111a2be4db5b12cb7bfa351bcebd5e94b0decb021fJim Grosbach 7121a2be4db5b12cb7bfa351bcebd5e94b0decb021fJim Grosbach logging.debug("%s: installing test url=%s", name, url) 7131a2be4db5b12cb7bfa351bcebd5e94b0decb021fJim Grosbach tarball = os.path.basename(url) 7141a2be4db5b12cb7bfa351bcebd5e94b0decb021fJim Grosbach tarball_path = os.path.join(group_dir, tarball) 7151a2be4db5b12cb7bfa351bcebd5e94b0decb021fJim Grosbach test_dir = os.path.join(group_dir, name) 7161a2be4db5b12cb7bfa351bcebd5e94b0decb021fJim Grosbach job.pkgmgr.fetch_pkg(tarball, tarball_path, 7171a2be4db5b12cb7bfa351bcebd5e94b0decb021fJim Grosbach repo_url = os.path.dirname(url)) 7182317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach 7192317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach # Create the directory for the test 7202317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach if not os.path.exists(test_dir): 7212317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach os.mkdir(os.path.join(group_dir, name)) 7222317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach 7232317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach job.pkgmgr.untar_pkg(tarball_path, test_dir) 7242317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach 7252317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach os.remove(tarball_path) 7262317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach 7272317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach # For this 'sub-object' to be importable via the name 7282317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach # 'group.name' we need to provide an __init__.py, 7292317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach # so link the main entry point to this. 7302317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach os.symlink(name + '.py', os.path.join(group_dir, name, 7312317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach '__init__.py')) 7322317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach 7332317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach # The test is now installed. 7342317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach return (group, name) 7352317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach 7362317fe1584e02582c616c1c4d15954999ff5525aJim Grosbach 73780d01dd3d19a84621324ac444c6749602df7a513Jim Grosbachdef _call_test_function(func, *args, **dargs): 73880d01dd3d19a84621324ac444c6749602df7a513Jim Grosbach """Calls a test function and translates exceptions so that errors 73980d01dd3d19a84621324ac444c6749602df7a513Jim Grosbach inside test code are considered test failures.""" 74080d01dd3d19a84621324ac444c6749602df7a513Jim Grosbach try: 74180d01dd3d19a84621324ac444c6749602df7a513Jim Grosbach return func(*args, **dargs) 74280d01dd3d19a84621324ac444c6749602df7a513Jim Grosbach except error.AutotestError: 74380d01dd3d19a84621324ac444c6749602df7a513Jim Grosbach raise 74480d01dd3d19a84621324ac444c6749602df7a513Jim Grosbach except Exception, e: 74580d01dd3d19a84621324ac444c6749602df7a513Jim Grosbach # Other exceptions must be treated as a FAIL when 74680d01dd3d19a84621324ac444c6749602df7a513Jim Grosbach # raised during the test functions 74780d01dd3d19a84621324ac444c6749602df7a513Jim Grosbach raise error.UnhandledTestFail(e) 74880d01dd3d19a84621324ac444c6749602df7a513Jim Grosbach 74980d01dd3d19a84621324ac444c6749602df7a513Jim Grosbach 7501a2be4db5b12cb7bfa351bcebd5e94b0decb021fJim Grosbachdef runtest(job, url, tag, args, dargs, 751b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach local_namespace={}, global_namespace={}, 752b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach before_test_hook=None, after_test_hook=None, 753b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach before_iteration_hook=None, after_iteration_hook=None): 754b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach local_namespace = local_namespace.copy() 755b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach global_namespace = global_namespace.copy() 756b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach # if this is not a plain test name then download and install the 757b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach # specified test 758b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach if url.endswith('.tar.bz2'): 759b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach (testgroup, testname) = _installtest(job, url) 760b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach bindir = os.path.join(job.testdir, 'download', testgroup, testname) 761b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach importdir = os.path.join(job.testdir, 'download') 762b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach modulename = '%s.%s' % (re.sub('/', '.', testgroup), testname) 763b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach classname = '%s.%s' % (modulename, testname) 764b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach path = testname 765b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach else: 766b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach # If the test is local, it may be under either testdir or site_testdir. 767b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach # Tests in site_testdir override tests defined in testdir 768b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach testname = path = url 769b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach testgroup = '' 770b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach path = re.sub(':', '/', testname) 771b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach modulename = os.path.basename(path) 772b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach classname = '%s.%s' % (modulename, modulename) 773b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach 774b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach # Try installing the test package 775b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach # The job object may be either a server side job or a client side job. 776b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach # 'install_pkg' method will be present only if it's a client side job. 777b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach if hasattr(job, 'install_pkg'): 778b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach try: 779b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach bindir = os.path.join(job.testdir, testname) 780b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach job.install_pkg(testname, 'test', bindir) 781b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach except error.PackageInstallError: 782b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach # continue as a fall back mechanism and see if the test code 783b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach # already exists on the machine 784b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach pass 785b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach 786b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach bindir = None 787b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach for dir in [job.testdir, getattr(job, 'site_testdir', None)]: 788b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach if dir is not None and os.path.exists(os.path.join(dir, path)): 789b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach importdir = bindir = os.path.join(dir, path) 790b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach if not bindir: 791b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach raise error.TestError(testname + ': test does not exist') 792b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach 793b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach subdir = os.path.join(dargs.pop('master_testpath', ""), testname) 794b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach outputdir = os.path.join(job.resultdir, subdir) 795b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach if tag: 796b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach outputdir += '.' + tag 797b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach 798b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach local_namespace['job'] = job 799b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach local_namespace['bindir'] = bindir 800b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach local_namespace['outputdir'] = outputdir 801b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach 802b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach sys.path.insert(0, importdir) 803b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach try: 804b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach exec ('import %s' % modulename, local_namespace, global_namespace) 805b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach exec ("mytest = %s(job, bindir, outputdir)" % classname, 806b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach local_namespace, global_namespace) 807b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach finally: 808b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach sys.path.pop(0) 809b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach 810b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach pwd = os.getcwd() 811b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach os.chdir(outputdir) 812b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach 813b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach try: 814b29b4dd988c50d5c4a15cd196e7910bf46f30b83Jim Grosbach mytest = global_namespace['mytest'] 815ab40f4b737b0a87c4048a9ad2f0c02be735e3770Jim Grosbach mytest.success = False 816ab40f4b737b0a87c4048a9ad2f0c02be735e3770Jim Grosbach if before_test_hook: 817ab40f4b737b0a87c4048a9ad2f0c02be735e3770Jim Grosbach before_test_hook(mytest) 818ab40f4b737b0a87c4048a9ad2f0c02be735e3770Jim Grosbach 819ab40f4b737b0a87c4048a9ad2f0c02be735e3770Jim Grosbach # we use the register iteration hooks methods to register the passed 820ab40f4b737b0a87c4048a9ad2f0c02be735e3770Jim Grosbach # in hooks 821ab40f4b737b0a87c4048a9ad2f0c02be735e3770Jim Grosbach if before_iteration_hook: 822ab40f4b737b0a87c4048a9ad2f0c02be735e3770Jim Grosbach mytest.register_before_iteration_hook(before_iteration_hook) 823ab40f4b737b0a87c4048a9ad2f0c02be735e3770Jim Grosbach if after_iteration_hook: 824ab40f4b737b0a87c4048a9ad2f0c02be735e3770Jim Grosbach mytest.register_after_iteration_hook(after_iteration_hook) 825ab40f4b737b0a87c4048a9ad2f0c02be735e3770Jim Grosbach mytest._exec(args, dargs) 826ab40f4b737b0a87c4048a9ad2f0c02be735e3770Jim Grosbach mytest.success = True 827ab40f4b737b0a87c4048a9ad2f0c02be735e3770Jim Grosbach finally: 828c3635c2e928a7ecde11398ff272411f6dea2dcd2Jim Grosbach os.chdir(pwd) 829c3635c2e928a7ecde11398ff272411f6dea2dcd2Jim Grosbach if after_test_hook: 830c3635c2e928a7ecde11398ff272411f6dea2dcd2Jim Grosbach after_test_hook(mytest) 831c3635c2e928a7ecde11398ff272411f6dea2dcd2Jim Grosbach shutil.rmtree(mytest.tmpdir, ignore_errors=True) 832c3635c2e928a7ecde11398ff272411f6dea2dcd2Jim Grosbach