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