blob: 82fd8febd410acf8ff1b745fe0e6ef1bbdd3fdcf [file] [log] [blame]
mbligh6231cd62008-02-02 19:18:33 +00001# Shell class for a test, inherited by all individual tests
2#
3# Methods:
jadmanski0afbb632008-06-06 21:10:57 +00004# __init__ initialise
5# initialize run once for each job
6# setup run once for each new version of the test installed
7# run run the test (wrapped by job.run_test())
mbligh6231cd62008-02-02 19:18:33 +00008#
9# Data:
jadmanski0afbb632008-06-06 21:10:57 +000010# job backreference to the job this test instance is part of
11# outputdir eg. results/<job>/<testname.tag>
12# resultsdir eg. results/<job>/<testname.tag>/results
13# profdir eg. results/<job>/<testname.tag>/profiling
14# debugdir eg. results/<job>/<testname.tag>/debug
15# bindir eg. tests/<test>
16# src eg. tests/<test>/src
jadmanski825e24c2008-08-27 20:54:31 +000017# tmpdir eg. tmp/<tempname>_<testname.tag>
mbligh6231cd62008-02-02 19:18:33 +000018
Aviv Keshet39164ca2013-03-27 15:08:33 -070019#pylint: disable-msg=C0111
20
Owen Lin9f852402014-04-15 16:35:05 +080021import fcntl, json, os, re, sys, shutil, stat, tempfile, time, traceback
Scott Zawalski91493c82013-01-25 16:15:20 -050022import logging
mbligh6231cd62008-02-02 19:18:33 +000023
jadmanskic27c2312009-08-05 20:58:51 +000024from autotest_lib.client.common_lib import error
mbligh53da18e2009-01-05 21:13:26 +000025from autotest_lib.client.bin import utils
mbligh6231cd62008-02-02 19:18:33 +000026
27
jadmanskiaafbf2a2010-06-25 17:07:24 +000028class base_test(object):
jadmanski0afbb632008-06-06 21:10:57 +000029 preserve_srcdir = False
jadmanski91d56a92009-04-01 15:20:40 +000030 network_destabilizing = False
mbligh6231cd62008-02-02 19:18:33 +000031
jadmanski0afbb632008-06-06 21:10:57 +000032 def __init__(self, job, bindir, outputdir):
33 self.job = job
mbligh21e33582009-02-04 18:18:31 +000034 self.pkgmgr = job.pkgmgr
jadmanski0afbb632008-06-06 21:10:57 +000035 self.autodir = job.autodir
jadmanski0afbb632008-06-06 21:10:57 +000036 self.outputdir = outputdir
showardb18134f2009-03-20 20:52:18 +000037 self.tagged_testname = os.path.basename(self.outputdir)
jadmanski0afbb632008-06-06 21:10:57 +000038 self.resultsdir = os.path.join(self.outputdir, 'results')
39 os.mkdir(self.resultsdir)
40 self.profdir = os.path.join(self.outputdir, 'profiling')
41 os.mkdir(self.profdir)
42 self.debugdir = os.path.join(self.outputdir, 'debug')
43 os.mkdir(self.debugdir)
Eric Li8b2954a2010-07-12 21:42:34 -070044 # TODO(ericli): figure out how autotest crash handler work with cros
Scott Zawalski91493c82013-01-25 16:15:20 -050045 # Once this is re-enabled import getpass. crosbug.com/31232
Eric Li8b2954a2010-07-12 21:42:34 -070046 # crash handler, we should restore it in near term.
Scott Zawalski91493c82013-01-25 16:15:20 -050047 # if getpass.getuser() == 'root':
48 # self.configure_crash_handler()
49 # else:
Eric Li8b2954a2010-07-12 21:42:34 -070050 self.crash_handling_enabled = False
jadmanski0afbb632008-06-06 21:10:57 +000051 self.bindir = bindir
jadmanski0afbb632008-06-06 21:10:57 +000052 self.srcdir = os.path.join(self.bindir, 'src')
mbligh5c1bb252009-03-25 22:06:49 +000053 self.tmpdir = tempfile.mkdtemp("_" + self.tagged_testname,
showardb18134f2009-03-20 20:52:18 +000054 dir=job.tmpdir)
mbligh7af09972009-04-17 22:17:08 +000055 self._keyvals = []
56 self._new_keyval = False
mbligh32cb5b42009-05-01 23:05:09 +000057 self.failed_constraints = []
mbligh5e703a22009-06-15 22:00:12 +000058 self.iteration = 0
mbligh742ae422009-05-13 20:46:41 +000059 self.before_iteration_hooks = []
60 self.after_iteration_hooks = []
mbligh6231cd62008-02-02 19:18:33 +000061
Dan Shi2ca97772013-10-21 17:17:27 -070062 # Flag to indicate if the test has succeeded or failed.
63 self.success = False
64
mbligh6231cd62008-02-02 19:18:33 +000065
mbligh6894ce22009-09-18 19:56:30 +000066 def configure_crash_handler(self):
lmrfb118872009-10-13 19:41:48 +000067 pass
mbligh6894ce22009-09-18 19:56:30 +000068
69
70 def crash_handler_report(self):
lmrfb118872009-10-13 19:41:48 +000071 pass
mbligh6894ce22009-09-18 19:56:30 +000072
73
jadmanski0afbb632008-06-06 21:10:57 +000074 def assert_(self, expr, msg='Assertion failed.'):
75 if not expr:
76 raise error.TestError(msg)
mbligh6231cd62008-02-02 19:18:33 +000077
78
jadmanski0afbb632008-06-06 21:10:57 +000079 def write_test_keyval(self, attr_dict):
Eric Li861b2d52011-02-04 14:50:35 -080080 utils.write_keyval(self.outputdir, attr_dict,
81 tap_report=self.job._tap)
jadmanskicc549172008-05-21 18:11:51 +000082
jadmanski0afbb632008-06-06 21:10:57 +000083 @staticmethod
84 def _append_type_to_keys(dictionary, typename):
85 new_dict = {}
86 for key, value in dictionary.iteritems():
87 new_key = "%s{%s}" % (key, typename)
88 new_dict[new_key] = value
89 return new_dict
jadmanskicc549172008-05-21 18:11:51 +000090
91
Fang Denge689e712013-11-13 18:27:06 -080092 def output_perf_value(self, description, value, units=None,
Fang Deng7f24f0b2013-11-12 11:22:16 -080093 higher_is_better=True, graph=None):
Dennis Jeffrey918863f2013-06-19 16:49:48 -070094 """
95 Records a measured performance value in an output file.
96
97 The output file will subsequently be parsed by the TKO parser to have
98 the information inserted into the results database.
99
100 @param description: A string describing the measured perf value. Must
101 be maximum length 256, and may only contain letters, numbers,
102 periods, dashes, and underscores. For example:
103 "page_load_time", "scrolling-frame-rate".
104 @param value: A number representing the measured perf value, or a list
105 of measured values if a test takes multiple measurements.
106 Measured perf values can be either ints or floats.
107 @param units: A string describing the units associated with the
108 measured perf value. Must be maximum length 32, and may only
109 contain letters, numbers, periods, dashes, and underscores.
110 For example: "msec", "fps", "score", "runs_per_second".
111 @param higher_is_better: A boolean indicating whether or not a "higher"
112 measured perf value is considered to be better. If False, it is
113 assumed that a "lower" measured value is considered to be
114 better.
Fang Deng7f24f0b2013-11-12 11:22:16 -0800115 @param graph: A string indicating the name of the graph on which
116 the perf value will be subsequently displayed on
117 the chrome perf dashboard.
118 This allows multiple metrics be grouped together
119 on the same graphs. Defaults to None, indicating
120 that the perf value should be displayed individually
121 on a separate graph.
Dennis Jeffrey918863f2013-06-19 16:49:48 -0700122
123 """
124 if len(description) > 256:
125 raise ValueError('The description must be at most 256 characters.')
126 if len(units) > 32:
127 raise ValueError('The units must be at most 32 characters.')
128 string_regex = re.compile(r'^[-\.\w]+$')
129 if (not string_regex.search(description) or
Fang Denge689e712013-11-13 18:27:06 -0800130 (units and not string_regex.search(units))):
Dennis Jeffrey918863f2013-06-19 16:49:48 -0700131 raise ValueError('Invalid description or units string. May only '
132 'contain letters, numbers, periods, dashes, and '
Fang Denge689e712013-11-13 18:27:06 -0800133 'underscores. description: %s, units: %s' %
134 (description, units))
Dennis Jeffrey918863f2013-06-19 16:49:48 -0700135
136 entry = {
137 'description': description,
138 'value': value,
139 'units': units,
140 'higher_is_better': higher_is_better,
Fang Deng7f24f0b2013-11-12 11:22:16 -0800141 'graph': graph
Dennis Jeffrey918863f2013-06-19 16:49:48 -0700142 }
143
144 output_path = os.path.join(self.resultsdir, 'perf_measurements')
145 with open(output_path, 'a') as fp:
146 fp.write(json.dumps(entry, sort_keys=True) + '\n')
147
148
mbligh0b3dd5f2008-07-16 20:37:13 +0000149 def write_perf_keyval(self, perf_dict):
Eric Li861b2d52011-02-04 14:50:35 -0800150 self.write_iteration_keyval({}, perf_dict,
151 tap_report=self.job._tap)
jadmanskicc549172008-05-21 18:11:51 +0000152
mbligh0b3dd5f2008-07-16 20:37:13 +0000153
154 def write_attr_keyval(self, attr_dict):
Eric Li861b2d52011-02-04 14:50:35 -0800155 self.write_iteration_keyval(attr_dict, {},
156 tap_report=self.job._tap)
mbligh0b3dd5f2008-07-16 20:37:13 +0000157
158
Eric Li861b2d52011-02-04 14:50:35 -0800159 def write_iteration_keyval(self, attr_dict, perf_dict, tap_report=None):
mbligh7af09972009-04-17 22:17:08 +0000160 # append the dictionaries before they have the {perf} and {attr} added
161 self._keyvals.append({'attr':attr_dict, 'perf':perf_dict})
162 self._new_keyval = True
163
mbligh0b3dd5f2008-07-16 20:37:13 +0000164 if attr_dict:
165 attr_dict = self._append_type_to_keys(attr_dict, "attr")
Eric Li861b2d52011-02-04 14:50:35 -0800166 utils.write_keyval(self.resultsdir, attr_dict, type_tag="attr",
167 tap_report=tap_report)
mbligh0b3dd5f2008-07-16 20:37:13 +0000168
169 if perf_dict:
170 perf_dict = self._append_type_to_keys(perf_dict, "perf")
Eric Li861b2d52011-02-04 14:50:35 -0800171 utils.write_keyval(self.resultsdir, perf_dict, type_tag="perf",
172 tap_report=tap_report)
jadmanskicc549172008-05-21 18:11:51 +0000173
jadmanski0afbb632008-06-06 21:10:57 +0000174 keyval_path = os.path.join(self.resultsdir, "keyval")
175 print >> open(keyval_path, "a"), ""
jadmanskicc549172008-05-21 18:11:51 +0000176
177
mbligh7af09972009-04-17 22:17:08 +0000178 def analyze_perf_constraints(self, constraints):
179 if not self._new_keyval:
180 return
181
jadmanski0d9ea772009-10-08 22:30:56 +0000182 # create a dict from the keyvals suitable as an environment for eval
183 keyval_env = self._keyvals[-1]['perf'].copy()
184 keyval_env['__builtins__'] = None
mbligh7af09972009-04-17 22:17:08 +0000185 self._new_keyval = False
mbligh32cb5b42009-05-01 23:05:09 +0000186 failures = []
jadmanski0d9ea772009-10-08 22:30:56 +0000187
188 # evaluate each constraint using the current keyvals
mbligh7af09972009-04-17 22:17:08 +0000189 for constraint in constraints:
jadmanski0d9ea772009-10-08 22:30:56 +0000190 logging.info('___________________ constraint = %s', constraint)
191 logging.info('___________________ keyvals = %s', keyval_env)
192
mbligh7af09972009-04-17 22:17:08 +0000193 try:
jadmanski0d9ea772009-10-08 22:30:56 +0000194 if not eval(constraint, keyval_env):
mbligh8beabca2009-05-21 01:33:15 +0000195 failures.append('%s: constraint was not met' % constraint)
mbligh7af09972009-04-17 22:17:08 +0000196 except:
mbligh32cb5b42009-05-01 23:05:09 +0000197 failures.append('could not evaluate constraint: %s'
198 % constraint)
mbligh7af09972009-04-17 22:17:08 +0000199
mbligh32cb5b42009-05-01 23:05:09 +0000200 # keep track of the errors for each iteration
201 self.failed_constraints.append(failures)
202
203
204 def process_failed_constraints(self):
205 msg = ''
206 for i, failures in enumerate(self.failed_constraints):
207 if failures:
208 msg += 'iteration %d:%s ' % (i, ','.join(failures))
209
210 if msg:
211 raise error.TestFail(msg)
mbligh7af09972009-04-17 22:17:08 +0000212
213
mbligh742ae422009-05-13 20:46:41 +0000214 def register_before_iteration_hook(self, iteration_hook):
215 """
216 This is how we expect test writers to register a before_iteration_hook.
217 This adds the method to the list of hooks which are executed
218 before each iteration.
219
220 @param iteration_hook: Method to run before each iteration. A valid
221 hook accepts a single argument which is the
222 test object.
223 """
224 self.before_iteration_hooks.append(iteration_hook)
225
226
227 def register_after_iteration_hook(self, iteration_hook):
228 """
229 This is how we expect test writers to register an after_iteration_hook.
230 This adds the method to the list of hooks which are executed
231 after each iteration.
232
233 @param iteration_hook: Method to run after each iteration. A valid
234 hook accepts a single argument which is the
235 test object.
236 """
237 self.after_iteration_hooks.append(iteration_hook)
238
239
jadmanski0afbb632008-06-06 21:10:57 +0000240 def initialize(self):
241 pass
mbligh6231cd62008-02-02 19:18:33 +0000242
243
jadmanski0afbb632008-06-06 21:10:57 +0000244 def setup(self):
245 pass
mbligh6231cd62008-02-02 19:18:33 +0000246
247
mbligh14f98562008-07-29 21:16:27 +0000248 def warmup(self, *args, **dargs):
mbligh4205d892008-07-14 16:23:20 +0000249 pass
mblighb53a3472008-07-11 21:27:58 +0000250
251
mblighb5dac432008-11-27 00:38:44 +0000252 def drop_caches_between_iterations(self):
253 if self.job.drop_caches_between_iterations:
mbligh53da18e2009-01-05 21:13:26 +0000254 utils.drop_caches()
mblighb5dac432008-11-27 00:38:44 +0000255
256
Scott Zawalski91493c82013-01-25 16:15:20 -0500257 def _call_run_once_with_retry(self, constraints, profile_only,
258 postprocess_profiled_run, args, dargs):
259 """Thin wrapper around _call_run_once that retries unsuccessful tests.
260
261 If the job object's attribute test_retry is > 0 retry any tests that
262 ran unsuccessfully X times.
263 *Note this does not competely re-initialize the test, it only
264 re-executes code once all the initial job set up (packages,
265 sysinfo, etc) is complete.
266 """
267 if self.job.test_retry != 0:
268 logging.info('Test will be retried a maximum of %d times',
269 self.job.test_retry)
270
271 max_runs = self.job.test_retry
272 for retry_run in xrange(0, max_runs+1):
273 try:
274 self._call_run_once(constraints, profile_only,
275 postprocess_profiled_run, args, dargs)
276 break
Aviv Keshet39164ca2013-03-27 15:08:33 -0700277 except error.TestFailRetry as err:
Scott Zawalski91493c82013-01-25 16:15:20 -0500278 if retry_run == max_runs:
279 raise
280 self.job.record('INFO', None, None, 'Run %s failed with %s' % (
281 retry_run, err))
282 if retry_run > 0:
283 self.write_test_keyval({'test_retries_before_success': retry_run})
284
285
mblighf58865f2009-05-13 21:32:42 +0000286 def _call_run_once(self, constraints, profile_only,
287 postprocess_profiled_run, args, dargs):
mbligh6b97f792009-03-23 21:23:12 +0000288 self.drop_caches_between_iterations()
mbligh4395bbd2009-03-25 19:34:17 +0000289 # execute iteration hooks
mbligh742ae422009-05-13 20:46:41 +0000290 for hook in self.before_iteration_hooks:
291 hook(self)
mblighf58865f2009-05-13 21:32:42 +0000292
Eric Lidaf6ff02011-03-01 15:31:31 -0800293 try:
294 if profile_only:
295 if not self.job.profilers.present():
296 self.job.record('WARN', None, None,
297 'No profilers have been added but '
298 'profile_only is set - nothing '
299 'will be run')
300 self.run_once_profiling(postprocess_profiled_run,
301 *args, **dargs)
302 else:
303 self.before_run_once()
304 self.run_once(*args, **dargs)
305 self.after_run_once()
mblighf58865f2009-05-13 21:32:42 +0000306
Eric Lidaf6ff02011-03-01 15:31:31 -0800307 self.postprocess_iteration()
308 self.analyze_perf_constraints(constraints)
309 finally:
310 for hook in self.after_iteration_hooks:
311 hook(self)
mbligh6b97f792009-03-23 21:23:12 +0000312
313
showarda6082ef2009-10-12 20:25:44 +0000314 def execute(self, iterations=None, test_length=None, profile_only=None,
mbligha49c5cb2009-02-26 01:01:09 +0000315 _get_time=time.time, postprocess_profiled_run=None,
mbligh7af09972009-04-17 22:17:08 +0000316 constraints=(), *args, **dargs):
mbligh777d96e2008-09-03 16:34:38 +0000317 """
318 This is the basic execute method for the tests inherited from base_test.
319 If you want to implement a benchmark test, it's better to implement
320 the run_once function, to cope with the profiling infrastructure. For
321 other tests, you can just override the default implementation.
mbligh60434712008-07-16 16:35:10 +0000322
mbligh777d96e2008-09-03 16:34:38 +0000323 @param test_length: The minimum test length in seconds. We'll run the
mbligh4b835b82009-02-11 01:26:13 +0000324 run_once function for a number of times large enough to cover the
325 minimum test length.
mbligh777d96e2008-09-03 16:34:38 +0000326
327 @param iterations: A number of iterations that we'll run the run_once
mbligh4b835b82009-02-11 01:26:13 +0000328 function. This parameter is incompatible with test_length and will
329 be silently ignored if you specify both.
330
mblighf58865f2009-05-13 21:32:42 +0000331 @param profile_only: If true run X iterations with profilers enabled.
showarda6082ef2009-10-12 20:25:44 +0000332 If false run X iterations and one with profiling if profiles are
333 enabled. If None, default to the value of job.default_profile_only.
mbligh4b835b82009-02-11 01:26:13 +0000334
335 @param _get_time: [time.time] Used for unit test time injection.
mblighc9314082009-02-26 00:48:18 +0000336
mbligha49c5cb2009-02-26 01:01:09 +0000337 @param postprocess_profiled_run: Run the postprocessing for the
338 profiled run.
mbligh777d96e2008-09-03 16:34:38 +0000339 """
340
341 # For our special class of tests, the benchmarks, we don't want
342 # profilers to run during the test iterations. Let's reserve only
343 # the last iteration for profiling, if needed. So let's stop
344 # all profilers if they are present and active.
mblighb3c0c912008-11-27 00:32:45 +0000345 profilers = self.job.profilers
346 if profilers.active():
mbligh777d96e2008-09-03 16:34:38 +0000347 profilers.stop(self)
showarda6082ef2009-10-12 20:25:44 +0000348 if profile_only is None:
349 profile_only = self.job.default_profile_only
mbligh777d96e2008-09-03 16:34:38 +0000350 # If the user called this test in an odd way (specified both iterations
mbligh4b835b82009-02-11 01:26:13 +0000351 # and test_length), let's warn them.
mbligh777d96e2008-09-03 16:34:38 +0000352 if iterations and test_length:
Dale Curtis456d3c12011-07-19 11:42:51 -0700353 logging.debug('Iterations parameter ignored (timed execution)')
mbligh777d96e2008-09-03 16:34:38 +0000354 if test_length:
mbligh4b835b82009-02-11 01:26:13 +0000355 test_start = _get_time()
mbligh777d96e2008-09-03 16:34:38 +0000356 time_elapsed = 0
357 timed_counter = 0
Dale Curtis456d3c12011-07-19 11:42:51 -0700358 logging.debug('Test started. Specified %d s as the minimum test '
359 'length', test_length)
mbligh777d96e2008-09-03 16:34:38 +0000360 while time_elapsed < test_length:
361 timed_counter = timed_counter + 1
362 if time_elapsed == 0:
Dale Curtis456d3c12011-07-19 11:42:51 -0700363 logging.debug('Executing iteration %d', timed_counter)
mbligh777d96e2008-09-03 16:34:38 +0000364 elif time_elapsed > 0:
Dale Curtis456d3c12011-07-19 11:42:51 -0700365 logging.debug('Executing iteration %d, time_elapsed %d s',
366 timed_counter, time_elapsed)
Scott Zawalski91493c82013-01-25 16:15:20 -0500367 self._call_run_once_with_retry(constraints, profile_only,
368 postprocess_profiled_run, args,
369 dargs)
mbligh4b835b82009-02-11 01:26:13 +0000370 test_iteration_finish = _get_time()
mbligh777d96e2008-09-03 16:34:38 +0000371 time_elapsed = test_iteration_finish - test_start
Dale Curtis456d3c12011-07-19 11:42:51 -0700372 logging.debug('Test finished after %d iterations, '
373 'time elapsed: %d s', timed_counter, time_elapsed)
mbligh777d96e2008-09-03 16:34:38 +0000374 else:
mblighf58865f2009-05-13 21:32:42 +0000375 if iterations is None:
mbligh777d96e2008-09-03 16:34:38 +0000376 iterations = 1
Dale Curtis456d3c12011-07-19 11:42:51 -0700377 if iterations > 1:
378 logging.debug('Test started. Specified %d iterations',
379 iterations)
380 for self.iteration in xrange(1, iterations + 1):
381 if iterations > 1:
382 logging.debug('Executing iteration %d of %d',
383 self.iteration, iterations)
Scott Zawalski91493c82013-01-25 16:15:20 -0500384 self._call_run_once_with_retry(constraints, profile_only,
385 postprocess_profiled_run, args,
386 dargs)
mbligh60434712008-07-16 16:35:10 +0000387
mblighf58865f2009-05-13 21:32:42 +0000388 if not profile_only:
mbligh5e703a22009-06-15 22:00:12 +0000389 self.iteration += 1
mblighf58865f2009-05-13 21:32:42 +0000390 self.run_once_profiling(postprocess_profiled_run, *args, **dargs)
mblighd27604e2009-02-03 02:06:08 +0000391
392 # Do any postprocessing, normally extracting performance keyvals, etc
393 self.postprocess()
mbligh32cb5b42009-05-01 23:05:09 +0000394 self.process_failed_constraints()
mblighd27604e2009-02-03 02:06:08 +0000395
396
mbligha49c5cb2009-02-26 01:01:09 +0000397 def run_once_profiling(self, postprocess_profiled_run, *args, **dargs):
mblighd27604e2009-02-03 02:06:08 +0000398 profilers = self.job.profilers
mbligh60434712008-07-16 16:35:10 +0000399 # Do a profiling run if necessary
mblighb3c0c912008-11-27 00:32:45 +0000400 if profilers.present():
mblighc9314082009-02-26 00:48:18 +0000401 self.drop_caches_between_iterations()
mbligh1b0faf92009-12-19 05:26:13 +0000402 profilers.before_start(self)
403
404 self.before_run_once()
mbligh60434712008-07-16 16:35:10 +0000405 profilers.start(self)
Dale Curtis456d3c12011-07-19 11:42:51 -0700406 logging.debug('Profilers present. Profiling run started')
mbligh1b0faf92009-12-19 05:26:13 +0000407
jadmanski0d390072008-11-19 21:19:56 +0000408 try:
409 self.run_once(*args, **dargs)
mbligha49c5cb2009-02-26 01:01:09 +0000410
411 # Priority to the run_once() argument over the attribute.
412 postprocess_attribute = getattr(self,
413 'postprocess_profiled_run',
414 False)
415
416 if (postprocess_profiled_run or
417 (postprocess_profiled_run is None and
418 postprocess_attribute)):
419 self.postprocess_iteration()
420
jadmanski0d390072008-11-19 21:19:56 +0000421 finally:
422 profilers.stop(self)
423 profilers.report(self)
mbligh60434712008-07-16 16:35:10 +0000424
mbligh1b0faf92009-12-19 05:26:13 +0000425 self.after_run_once()
426
mbligh60434712008-07-16 16:35:10 +0000427
428 def postprocess(self):
429 pass
430
431
mbligh34b297b2009-02-03 17:49:48 +0000432 def postprocess_iteration(self):
433 pass
434
435
mbligh60434712008-07-16 16:35:10 +0000436 def cleanup(self):
437 pass
mblighcd8a5162008-07-16 16:32:12 +0000438
439
mbligh1b0faf92009-12-19 05:26:13 +0000440 def before_run_once(self):
441 """
442 Override in tests that need it, will be called before any run_once()
443 call including the profiling run (when it's called before starting
444 the profilers).
445 """
446 pass
447
448
449 def after_run_once(self):
450 """
451 Called after every run_once (including from a profiled run when it's
452 called after stopping the profilers).
453 """
454 pass
455
456
Owen Lin9f852402014-04-15 16:35:05 +0800457 @staticmethod
458 def _make_writable_to_others(directory):
459 mode = os.stat(directory).st_mode
460 mode = mode | stat.S_IROTH | stat.S_IWOTH | stat.S_IXOTH
461 os.chmod(directory, mode)
462
463
mbligh742ae422009-05-13 20:46:41 +0000464 def _exec(self, args, dargs):
showardee36bc72009-06-18 23:13:53 +0000465 self.job.logging.tee_redirect_debug_dir(self.debugdir,
466 log_name=self.tagged_testname)
jadmanski0afbb632008-06-06 21:10:57 +0000467 try:
jadmanski91d56a92009-04-01 15:20:40 +0000468 if self.network_destabilizing:
469 self.job.disable_warnings("NETWORK")
470
jadmanski62655782008-07-28 21:27:46 +0000471 # write out the test attributes into a keyval
472 dargs = dargs.copy()
jadmanski23afbec2008-09-17 18:12:07 +0000473 run_cleanup = dargs.pop('run_cleanup', self.job.run_test_cleanup)
mbligh234a84f2008-11-20 19:57:43 +0000474 keyvals = dargs.pop('test_attributes', {}).copy()
jadmanski62655782008-07-28 21:27:46 +0000475 keyvals['version'] = self.version
jadmanski2ae0e052008-09-04 16:37:28 +0000476 for i, arg in enumerate(args):
477 keyvals['param-%d' % i] = repr(arg)
478 for name, arg in dargs.iteritems():
479 keyvals['param-%s' % name] = repr(arg)
jadmanski62655782008-07-28 21:27:46 +0000480 self.write_test_keyval(keyvals)
481
mblighcf238192008-07-17 01:18:44 +0000482 _validate_args(args, dargs, self.initialize, self.setup,
483 self.execute, self.cleanup)
mbligh6231cd62008-02-02 19:18:33 +0000484
jadmanski0afbb632008-06-06 21:10:57 +0000485 try:
Owen Lin9f852402014-04-15 16:35:05 +0800486 # Make resultsdir and tmpdir accessible to everyone. We may
487 # output data to these directories as others, e.g., chronos.
488 self._make_writable_to_others(self.tmpdir)
489 self._make_writable_to_others(self.resultsdir)
490
mblighcf238192008-07-17 01:18:44 +0000491 # Initialize:
mbligh5c1bb252009-03-25 22:06:49 +0000492 _cherry_pick_call(self.initialize, *args, **dargs)
mblighcf238192008-07-17 01:18:44 +0000493
mblighc5ddfd12008-08-04 17:15:00 +0000494 lockfile = open(os.path.join(self.job.tmpdir, '.testlock'), 'w')
495 try:
496 fcntl.flock(lockfile, fcntl.LOCK_EX)
497 # Setup: (compile and install the test, if needed)
498 p_args, p_dargs = _cherry_pick_args(self.setup,args,dargs)
499 utils.update_version(self.srcdir, self.preserve_srcdir,
500 self.version, self.setup,
501 *p_args, **p_dargs)
502 finally:
503 fcntl.flock(lockfile, fcntl.LOCK_UN)
504 lockfile.close()
mblighcf238192008-07-17 01:18:44 +0000505
mblighcf238192008-07-17 01:18:44 +0000506 # Execute:
jadmanski62655782008-07-28 21:27:46 +0000507 os.chdir(self.outputdir)
mbligh4395bbd2009-03-25 19:34:17 +0000508
mbligh5c1bb252009-03-25 22:06:49 +0000509 # call self.warmup cherry picking the arguments it accepts and
510 # translate exceptions if needed
511 _call_test_function(_cherry_pick_call, self.warmup,
512 *args, **dargs)
513
mblighcf238192008-07-17 01:18:44 +0000514 if hasattr(self, 'run_once'):
515 p_args, p_dargs = _cherry_pick_args(self.run_once,
516 args, dargs)
jadmanski886c81f2009-02-19 12:54:03 +0000517 # pull in any non-* and non-** args from self.execute
518 for param in _get_nonstar_args(self.execute):
519 if param in dargs:
520 p_dargs[param] = dargs[param]
mblighcf238192008-07-17 01:18:44 +0000521 else:
522 p_args, p_dargs = _cherry_pick_args(self.execute,
523 args, dargs)
mbligh5c1bb252009-03-25 22:06:49 +0000524
525 _call_test_function(self.execute, *p_args, **p_dargs)
mbligh234a84f2008-11-20 19:57:43 +0000526 except Exception:
Dan Shi1d8803b2014-06-19 14:32:00 -0700527 utils.take_screenshot(self.debugdir,
528 '%s-fail' % self.tagged_testname)
mbligh234a84f2008-11-20 19:57:43 +0000529 # Save the exception while we run our cleanup() before
Prathmesh Prabhuac1a4c52014-08-20 17:26:54 -0700530 # reraising it, but log it to so actual time of error is known.
jadmanskid625c7f2008-08-27 14:08:52 +0000531 exc_info = sys.exc_info()
Prathmesh Prabhuac1a4c52014-08-20 17:26:54 -0700532 logging.warning('Autotest caught exception when running test:',
533 exc_info=True)
534
mbligh234a84f2008-11-20 19:57:43 +0000535 try:
536 try:
537 if run_cleanup:
mbligh5c1bb252009-03-25 22:06:49 +0000538 _cherry_pick_call(self.cleanup, *args, **dargs)
mbligh234a84f2008-11-20 19:57:43 +0000539 except Exception:
Dale Curtis456d3c12011-07-19 11:42:51 -0700540 logging.error('Ignoring exception during cleanup() phase:')
mbligh234a84f2008-11-20 19:57:43 +0000541 traceback.print_exc()
Dale Curtis456d3c12011-07-19 11:42:51 -0700542 logging.error('Now raising the earlier %s error',
543 exc_info[0])
mbligh6894ce22009-09-18 19:56:30 +0000544 self.crash_handler_report()
mbligh234a84f2008-11-20 19:57:43 +0000545 finally:
showard75cdfee2009-06-10 17:40:41 +0000546 self.job.logging.restore()
mbligh234a84f2008-11-20 19:57:43 +0000547 try:
548 raise exc_info[0], exc_info[1], exc_info[2]
549 finally:
550 # http://docs.python.org/library/sys.html#sys.exc_info
551 # Be nice and prevent a circular reference.
552 del exc_info
jadmanskid625c7f2008-08-27 14:08:52 +0000553 else:
mbligh234a84f2008-11-20 19:57:43 +0000554 try:
555 if run_cleanup:
mbligh5c1bb252009-03-25 22:06:49 +0000556 _cherry_pick_call(self.cleanup, *args, **dargs)
mbligh6894ce22009-09-18 19:56:30 +0000557 self.crash_handler_report()
mbligh234a84f2008-11-20 19:57:43 +0000558 finally:
showard75cdfee2009-06-10 17:40:41 +0000559 self.job.logging.restore()
jadmanski0afbb632008-06-06 21:10:57 +0000560 except error.AutotestError:
jadmanski91d56a92009-04-01 15:20:40 +0000561 if self.network_destabilizing:
562 self.job.enable_warnings("NETWORK")
mbligh234a84f2008-11-20 19:57:43 +0000563 # Pass already-categorized errors on up.
jadmanski0afbb632008-06-06 21:10:57 +0000564 raise
565 except Exception, e:
jadmanski91d56a92009-04-01 15:20:40 +0000566 if self.network_destabilizing:
567 self.job.enable_warnings("NETWORK")
mbligh234a84f2008-11-20 19:57:43 +0000568 # Anything else is an ERROR in our own code, not execute().
mblighc2180832008-07-25 03:26:12 +0000569 raise error.UnhandledTestError(e)
jadmanski91d56a92009-04-01 15:20:40 +0000570 else:
571 if self.network_destabilizing:
572 self.job.enable_warnings("NETWORK")
mbligh6231cd62008-02-02 19:18:33 +0000573
574
Dale Curtis74a314b2011-06-23 14:55:46 -0700575 def runsubtest(self, url, *args, **dargs):
576 """
577 Execute another autotest test from inside the current test's scope.
578
579 @param test: Parent test.
580 @param url: Url of new test.
581 @param tag: Tag added to test name.
582 @param args: Args for subtest.
583 @param dargs: Dictionary with args for subtest.
584 @iterations: Number of subtest iterations.
585 @profile_only: If true execute one profiled run.
586 """
587 dargs["profile_only"] = dargs.get("profile_only", False)
588 test_basepath = self.outputdir[len(self.job.resultdir + "/"):]
589 return self.job.run_test(url, master_testpath=test_basepath,
590 *args, **dargs)
591
592
jadmanski886c81f2009-02-19 12:54:03 +0000593def _get_nonstar_args(func):
594 """Extract all the (normal) function parameter names.
595
596 Given a function, returns a tuple of parameter names, specifically
597 excluding the * and ** parameters, if the function accepts them.
598
599 @param func: A callable that we want to chose arguments for.
600
601 @return: A tuple of parameters accepted by the function.
602 """
603 return func.func_code.co_varnames[:func.func_code.co_argcount]
604
605
mblighcf238192008-07-17 01:18:44 +0000606def _cherry_pick_args(func, args, dargs):
mbligh234a84f2008-11-20 19:57:43 +0000607 """Sanitize positional and keyword arguments before calling a function.
608
609 Given a callable (func), an argument tuple and a dictionary of keyword
610 arguments, pick only those arguments which the function is prepared to
611 accept and return a new argument tuple and keyword argument dictionary.
612
613 Args:
614 func: A callable that we want to choose arguments for.
615 args: A tuple of positional arguments to consider passing to func.
616 dargs: A dictionary of keyword arguments to consider passing to func.
617 Returns:
618 A tuple of: (args tuple, keyword arguments dictionary)
619 """
mblighcf238192008-07-17 01:18:44 +0000620 # Cherry pick args:
621 if func.func_code.co_flags & 0x04:
622 # func accepts *args, so return the entire args.
623 p_args = args
624 else:
625 p_args = ()
626
627 # Cherry pick dargs:
628 if func.func_code.co_flags & 0x08:
629 # func accepts **dargs, so return the entire dargs.
630 p_dargs = dargs
631 else:
mbligh234a84f2008-11-20 19:57:43 +0000632 # Only return the keyword arguments that func accepts.
mblighcf238192008-07-17 01:18:44 +0000633 p_dargs = {}
jadmanski886c81f2009-02-19 12:54:03 +0000634 for param in _get_nonstar_args(func):
mblighcf238192008-07-17 01:18:44 +0000635 if param in dargs:
636 p_dargs[param] = dargs[param]
637
638 return p_args, p_dargs
639
640
mbligh5c1bb252009-03-25 22:06:49 +0000641def _cherry_pick_call(func, *args, **dargs):
642 """Cherry picks arguments from args/dargs based on what "func" accepts
643 and calls the function with the picked arguments."""
644 p_args, p_dargs = _cherry_pick_args(func, args, dargs)
645 return func(*p_args, **p_dargs)
646
647
mblighcf238192008-07-17 01:18:44 +0000648def _validate_args(args, dargs, *funcs):
mbligh234a84f2008-11-20 19:57:43 +0000649 """Verify that arguments are appropriate for at least one callable.
650
651 Given a list of callables as additional parameters, verify that
652 the proposed keyword arguments in dargs will each be accepted by at least
653 one of the callables.
654
655 NOTE: args is currently not supported and must be empty.
656
657 Args:
658 args: A tuple of proposed positional arguments.
659 dargs: A dictionary of proposed keyword arguments.
660 *funcs: Callables to be searched for acceptance of args and dargs.
661 Raises:
662 error.AutotestError: if an arg won't be accepted by any of *funcs.
663 """
mblighcf238192008-07-17 01:18:44 +0000664 all_co_flags = 0
665 all_varnames = ()
666 for func in funcs:
667 all_co_flags |= func.func_code.co_flags
668 all_varnames += func.func_code.co_varnames[:func.func_code.co_argcount]
669
670 # Check if given args belongs to at least one of the methods below.
671 if len(args) > 0:
672 # Current implementation doesn't allow the use of args.
mbligh234a84f2008-11-20 19:57:43 +0000673 raise error.TestError('Unnamed arguments not accepted. Please '
674 'call job.run_test with named args only')
mblighcf238192008-07-17 01:18:44 +0000675
676 # Check if given dargs belongs to at least one of the methods below.
677 if len(dargs) > 0:
678 if not all_co_flags & 0x08:
679 # no func accepts *dargs, so:
680 for param in dargs:
681 if not param in all_varnames:
682 raise error.AutotestError('Unknown parameter: %s' % param)
683
684
mbligh6231cd62008-02-02 19:18:33 +0000685def _installtest(job, url):
mblighc5ddfd12008-08-04 17:15:00 +0000686 (group, name) = job.pkgmgr.get_package_name(url, 'test')
mbligh6231cd62008-02-02 19:18:33 +0000687
jadmanski0afbb632008-06-06 21:10:57 +0000688 # Bail if the test is already installed
689 group_dir = os.path.join(job.testdir, "download", group)
690 if os.path.exists(os.path.join(group_dir, name)):
691 return (group, name)
mbligh6231cd62008-02-02 19:18:33 +0000692
jadmanski0afbb632008-06-06 21:10:57 +0000693 # If the group directory is missing create it and add
694 # an empty __init__.py so that sub-directories are
695 # considered for import.
696 if not os.path.exists(group_dir):
lmr23421722010-06-17 17:51:07 +0000697 os.makedirs(group_dir)
jadmanski0afbb632008-06-06 21:10:57 +0000698 f = file(os.path.join(group_dir, '__init__.py'), 'w+')
699 f.close()
mbligh6231cd62008-02-02 19:18:33 +0000700
Dale Curtis456d3c12011-07-19 11:42:51 -0700701 logging.debug("%s: installing test url=%s", name, url)
mblighc5ddfd12008-08-04 17:15:00 +0000702 tarball = os.path.basename(url)
703 tarball_path = os.path.join(group_dir, tarball)
704 test_dir = os.path.join(group_dir, name)
705 job.pkgmgr.fetch_pkg(tarball, tarball_path,
706 repo_url = os.path.dirname(url))
707
708 # Create the directory for the test
709 if not os.path.exists(test_dir):
710 os.mkdir(os.path.join(group_dir, name))
711
712 job.pkgmgr.untar_pkg(tarball_path, test_dir)
713
714 os.remove(tarball_path)
mbligh6231cd62008-02-02 19:18:33 +0000715
jadmanski0afbb632008-06-06 21:10:57 +0000716 # For this 'sub-object' to be importable via the name
717 # 'group.name' we need to provide an __init__.py,
718 # so link the main entry point to this.
719 os.symlink(name + '.py', os.path.join(group_dir, name,
720 '__init__.py'))
mbligh6231cd62008-02-02 19:18:33 +0000721
jadmanski0afbb632008-06-06 21:10:57 +0000722 # The test is now installed.
723 return (group, name)
mbligh6231cd62008-02-02 19:18:33 +0000724
725
mbligh5c1bb252009-03-25 22:06:49 +0000726def _call_test_function(func, *args, **dargs):
727 """Calls a test function and translates exceptions so that errors
728 inside test code are considered test failures."""
729 try:
730 return func(*args, **dargs)
731 except error.AutotestError:
mbligh5c1bb252009-03-25 22:06:49 +0000732 raise
733 except Exception, e:
734 # Other exceptions must be treated as a FAIL when
735 # raised during the test functions
736 raise error.UnhandledTestFail(e)
737
738
mbligh6231cd62008-02-02 19:18:33 +0000739def runtest(job, url, tag, args, dargs,
jadmanski30e9b592008-09-25 19:51:57 +0000740 local_namespace={}, global_namespace={},
mbligh4395bbd2009-03-25 19:34:17 +0000741 before_test_hook=None, after_test_hook=None,
742 before_iteration_hook=None, after_iteration_hook=None):
jadmanski0afbb632008-06-06 21:10:57 +0000743 local_namespace = local_namespace.copy()
744 global_namespace = global_namespace.copy()
jadmanski0afbb632008-06-06 21:10:57 +0000745 # if this is not a plain test name then download and install the
746 # specified test
mblighc5ddfd12008-08-04 17:15:00 +0000747 if url.endswith('.tar.bz2'):
mbligh620ccf02010-03-26 17:44:29 +0000748 (testgroup, testname) = _installtest(job, url)
749 bindir = os.path.join(job.testdir, 'download', testgroup, testname)
lmr23421722010-06-17 17:51:07 +0000750 importdir = os.path.join(job.testdir, 'download')
jadmanski0afbb632008-06-06 21:10:57 +0000751 site_bindir = None
mbligh620ccf02010-03-26 17:44:29 +0000752 modulename = '%s.%s' % (re.sub('/', '.', testgroup), testname)
753 classname = '%s.%s' % (modulename, testname)
754 path = testname
jadmanski0afbb632008-06-06 21:10:57 +0000755 else:
mbligh620ccf02010-03-26 17:44:29 +0000756 # If the test is local, it may be under either testdir or site_testdir.
757 # Tests in site_testdir override tests defined in testdir
758 testname = path = url
759 testgroup = ''
jadmanski06767042010-03-29 18:28:33 +0000760 path = re.sub(':', '/', testname)
mbligh620ccf02010-03-26 17:44:29 +0000761 modulename = os.path.basename(path)
762 classname = '%s.%s' % (modulename, modulename)
mbligh6231cd62008-02-02 19:18:33 +0000763
mbligh620ccf02010-03-26 17:44:29 +0000764 # Try installing the test package
765 # The job object may be either a server side job or a client side job.
766 # 'install_pkg' method will be present only if it's a client side job.
mblighc5ddfd12008-08-04 17:15:00 +0000767 if hasattr(job, 'install_pkg'):
768 try:
mbligh620ccf02010-03-26 17:44:29 +0000769 bindir = os.path.join(job.testdir, testname)
mblighc5ddfd12008-08-04 17:15:00 +0000770 job.install_pkg(testname, 'test', bindir)
jadmanskic27c2312009-08-05 20:58:51 +0000771 except error.PackageInstallError, e:
mblighc5ddfd12008-08-04 17:15:00 +0000772 # continue as a fall back mechanism and see if the test code
773 # already exists on the machine
774 pass
775
mbligh620ccf02010-03-26 17:44:29 +0000776 bindir = testdir = None
777 for dir in [job.testdir, getattr(job, 'site_testdir', None)]:
778 if dir is not None and os.path.exists(os.path.join(dir, path)):
779 testdir = dir
780 importdir = bindir = os.path.join(dir, path)
781 if not bindir:
782 raise error.TestError(testname + ': test does not exist')
783
Dale Curtis74a314b2011-06-23 14:55:46 -0700784 subdir = os.path.join(dargs.pop('master_testpath', ""), testname)
785 outputdir = os.path.join(job.resultdir, subdir)
jadmanski0afbb632008-06-06 21:10:57 +0000786 if tag:
787 outputdir += '.' + tag
mbligh6231cd62008-02-02 19:18:33 +0000788
mblighc5ddfd12008-08-04 17:15:00 +0000789 local_namespace['job'] = job
790 local_namespace['bindir'] = bindir
791 local_namespace['outputdir'] = outputdir
792
lmr23421722010-06-17 17:51:07 +0000793 sys.path.insert(0, importdir)
jadmanski0afbb632008-06-06 21:10:57 +0000794 try:
mbligh620ccf02010-03-26 17:44:29 +0000795 exec ('import %s' % modulename, local_namespace, global_namespace)
796 exec ("mytest = %s(job, bindir, outputdir)" % classname,
jadmanski0afbb632008-06-06 21:10:57 +0000797 local_namespace, global_namespace)
798 finally:
jadmanski0afbb632008-06-06 21:10:57 +0000799 sys.path.pop(0)
mbligh6231cd62008-02-02 19:18:33 +0000800
jadmanski0afbb632008-06-06 21:10:57 +0000801 pwd = os.getcwd()
802 os.chdir(outputdir)
mbligh4395bbd2009-03-25 19:34:17 +0000803
jadmanski0afbb632008-06-06 21:10:57 +0000804 try:
805 mytest = global_namespace['mytest']
Dan Shi2ca97772013-10-21 17:17:27 -0700806 mytest.success = False
jadmanski30e9b592008-09-25 19:51:57 +0000807 if before_test_hook:
808 before_test_hook(mytest)
mbligh742ae422009-05-13 20:46:41 +0000809
810 # we use the register iteration hooks methods to register the passed
811 # in hooks
812 if before_iteration_hook:
813 mytest.register_before_iteration_hook(before_iteration_hook)
814 if after_iteration_hook:
815 mytest.register_after_iteration_hook(after_iteration_hook)
816 mytest._exec(args, dargs)
Dan Shi2ca97772013-10-21 17:17:27 -0700817 mytest.success = True
jadmanski0afbb632008-06-06 21:10:57 +0000818 finally:
jadmanski213b02b2008-08-26 20:51:58 +0000819 os.chdir(pwd)
jadmanski0afbb632008-06-06 21:10:57 +0000820 if after_test_hook:
821 after_test_hook(mytest)
jadmanski825e24c2008-08-27 20:54:31 +0000822 shutil.rmtree(mytest.tmpdir, ignore_errors=True)