blob: f2ee0a668452c849646d56acd9f4ff5de9a02d97 [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
mbligh234a84f2008-11-20 19:57:43 +000019import fcntl, os, re, sys, shutil, tarfile, tempfile, time, traceback
mbligh6894ce22009-09-18 19:56:30 +000020import warnings, logging, glob, resource
mbligh6231cd62008-02-02 19:18:33 +000021
jadmanskic27c2312009-08-05 20:58:51 +000022from autotest_lib.client.common_lib import error
mbligh53da18e2009-01-05 21:13:26 +000023from autotest_lib.client.bin import utils
mbligh6231cd62008-02-02 19:18:33 +000024
25
26class base_test:
jadmanski0afbb632008-06-06 21:10:57 +000027 preserve_srcdir = False
jadmanski91d56a92009-04-01 15:20:40 +000028 network_destabilizing = False
mbligh6231cd62008-02-02 19:18:33 +000029
jadmanski0afbb632008-06-06 21:10:57 +000030 def __init__(self, job, bindir, outputdir):
31 self.job = job
mbligh21e33582009-02-04 18:18:31 +000032 self.pkgmgr = job.pkgmgr
jadmanski0afbb632008-06-06 21:10:57 +000033 self.autodir = job.autodir
jadmanski0afbb632008-06-06 21:10:57 +000034 self.outputdir = outputdir
showardb18134f2009-03-20 20:52:18 +000035 self.tagged_testname = os.path.basename(self.outputdir)
jadmanski0afbb632008-06-06 21:10:57 +000036 self.resultsdir = os.path.join(self.outputdir, 'results')
37 os.mkdir(self.resultsdir)
38 self.profdir = os.path.join(self.outputdir, 'profiling')
39 os.mkdir(self.profdir)
40 self.debugdir = os.path.join(self.outputdir, 'debug')
41 os.mkdir(self.debugdir)
mbligh6894ce22009-09-18 19:56:30 +000042 self.configure_crash_handler()
jadmanski0afbb632008-06-06 21:10:57 +000043 self.bindir = bindir
44 if hasattr(job, 'libdir'):
45 self.libdir = job.libdir
46 self.srcdir = os.path.join(self.bindir, 'src')
mbligh5c1bb252009-03-25 22:06:49 +000047 self.tmpdir = tempfile.mkdtemp("_" + self.tagged_testname,
showardb18134f2009-03-20 20:52:18 +000048 dir=job.tmpdir)
mbligh7af09972009-04-17 22:17:08 +000049 self._keyvals = []
50 self._new_keyval = False
mbligh32cb5b42009-05-01 23:05:09 +000051 self.failed_constraints = []
mbligh5e703a22009-06-15 22:00:12 +000052 self.iteration = 0
mbligh742ae422009-05-13 20:46:41 +000053 self.before_iteration_hooks = []
54 self.after_iteration_hooks = []
mbligh6231cd62008-02-02 19:18:33 +000055
mbligh6231cd62008-02-02 19:18:33 +000056
mbligh6894ce22009-09-18 19:56:30 +000057 def configure_crash_handler(self):
58 """
59 Configure the crash handler by:
60 * Setting up core size to unlimited
61 * Putting an appropriate crash handler on /proc/sys/kernel/core_pattern
62 * Create files that the crash handler will use to figure which tests
63 are active at a given moment
64
65 The crash handler will pick up the core file and write it to
66 self.debugdir, and perform analysis on it to generate a report. The
67 program also outputs some results to syslog.
68
69 If multiple tests are running, an attempt to verify if we still have
70 the old PID on the system process table to determine whether it is a
71 parent of the current test execution. If we can't determine it, the
72 core file and the report file will be copied to all test debug dirs.
73 """
74 self.pattern_file = '/proc/sys/kernel/core_pattern'
75 try:
76 # Enable core dumps
77 resource.setrlimit(resource.RLIMIT_CORE, (-1, -1))
78 # Trying to backup core pattern and register our script
79 self.core_pattern_backup = open(self.pattern_file, 'r').read()
80 pattern_file = open(self.pattern_file, 'w')
81 tools_dir = os.path.join(self.autodir, 'tools')
82 crash_handler_path = os.path.join(tools_dir, 'crash_handler.py')
83 pattern_file.write('|' + crash_handler_path + ' %p %t %u %s %h %e')
84 # Writing the files that the crash handler is going to use
85 self.debugdir_tmp_file = ('/tmp/autotest_results_dir.%s' %
86 os.getpid())
87 utils.open_write_close(self.debugdir_tmp_file, self.debugdir + "\n")
88 except Exception, e:
89 self.crash_handling_enabled = False
90 logging.error('Crash handling system disabled: %s' % e)
91 else:
92 self.crash_handling_enabled = True
93 logging.debug('Crash handling system enabled.')
94
95
96 def crash_handler_report(self):
97 """
98 If core dumps are found on the debugdir after the execution of the
99 test, let the user know.
100 """
101 if self.crash_handling_enabled:
102 core_dirs = glob.glob('%s/crash.*' % self.debugdir)
103 if core_dirs:
104 logging.warning('Programs crashed during test execution:')
105 for dir in core_dirs:
106 logging.warning('Please verify %s for more info', dir)
107 # Remove the debugdir info file
108 os.unlink(self.debugdir_tmp_file)
109 # Restore the core pattern backup
110 try:
111 utils.open_write_close(self.pattern_file,
112 self.core_pattern_backup)
113 except EnvironmentError:
114 pass
115
116
jadmanski0afbb632008-06-06 21:10:57 +0000117 def assert_(self, expr, msg='Assertion failed.'):
118 if not expr:
119 raise error.TestError(msg)
mbligh6231cd62008-02-02 19:18:33 +0000120
121
jadmanski0afbb632008-06-06 21:10:57 +0000122 def write_test_keyval(self, attr_dict):
123 utils.write_keyval(self.outputdir, attr_dict)
jadmanskicc549172008-05-21 18:11:51 +0000124
125
jadmanski0afbb632008-06-06 21:10:57 +0000126 @staticmethod
127 def _append_type_to_keys(dictionary, typename):
128 new_dict = {}
129 for key, value in dictionary.iteritems():
130 new_key = "%s{%s}" % (key, typename)
131 new_dict[new_key] = value
132 return new_dict
jadmanskicc549172008-05-21 18:11:51 +0000133
134
mbligh0b3dd5f2008-07-16 20:37:13 +0000135 def write_perf_keyval(self, perf_dict):
136 self.write_iteration_keyval({}, perf_dict)
jadmanskicc549172008-05-21 18:11:51 +0000137
mbligh0b3dd5f2008-07-16 20:37:13 +0000138
139 def write_attr_keyval(self, attr_dict):
140 self.write_iteration_keyval(attr_dict, {})
141
142
143 def write_iteration_keyval(self, attr_dict, perf_dict):
mbligh7af09972009-04-17 22:17:08 +0000144 # append the dictionaries before they have the {perf} and {attr} added
145 self._keyvals.append({'attr':attr_dict, 'perf':perf_dict})
146 self._new_keyval = True
147
mbligh0b3dd5f2008-07-16 20:37:13 +0000148 if attr_dict:
149 attr_dict = self._append_type_to_keys(attr_dict, "attr")
150 utils.write_keyval(self.resultsdir, attr_dict, type_tag="attr")
151
152 if perf_dict:
153 perf_dict = self._append_type_to_keys(perf_dict, "perf")
154 utils.write_keyval(self.resultsdir, perf_dict, type_tag="perf")
jadmanskicc549172008-05-21 18:11:51 +0000155
jadmanski0afbb632008-06-06 21:10:57 +0000156 keyval_path = os.path.join(self.resultsdir, "keyval")
157 print >> open(keyval_path, "a"), ""
jadmanskicc549172008-05-21 18:11:51 +0000158
159
mbligh7af09972009-04-17 22:17:08 +0000160 def analyze_perf_constraints(self, constraints):
161 if not self._new_keyval:
162 return
163
164 self._new_keyval = False
mbligh32cb5b42009-05-01 23:05:09 +0000165 failures = []
mbligh7af09972009-04-17 22:17:08 +0000166 for constraint in constraints:
167 print "___________________ constraint = %s" % constraint
168 print "___________________ keyvals = %s" % self._keyvals[-1]['perf']
169 try:
170 if not eval(constraint, self._keyvals[-1]['perf']):
mbligh8beabca2009-05-21 01:33:15 +0000171 failures.append('%s: constraint was not met' % constraint)
mbligh7af09972009-04-17 22:17:08 +0000172 except:
mbligh32cb5b42009-05-01 23:05:09 +0000173 failures.append('could not evaluate constraint: %s'
174 % constraint)
mbligh7af09972009-04-17 22:17:08 +0000175
mbligh32cb5b42009-05-01 23:05:09 +0000176 # keep track of the errors for each iteration
177 self.failed_constraints.append(failures)
178
179
180 def process_failed_constraints(self):
181 msg = ''
182 for i, failures in enumerate(self.failed_constraints):
183 if failures:
184 msg += 'iteration %d:%s ' % (i, ','.join(failures))
185
186 if msg:
187 raise error.TestFail(msg)
mbligh7af09972009-04-17 22:17:08 +0000188
189
mbligh742ae422009-05-13 20:46:41 +0000190 def register_before_iteration_hook(self, iteration_hook):
191 """
192 This is how we expect test writers to register a before_iteration_hook.
193 This adds the method to the list of hooks which are executed
194 before each iteration.
195
196 @param iteration_hook: Method to run before each iteration. A valid
197 hook accepts a single argument which is the
198 test object.
199 """
200 self.before_iteration_hooks.append(iteration_hook)
201
202
203 def register_after_iteration_hook(self, iteration_hook):
204 """
205 This is how we expect test writers to register an after_iteration_hook.
206 This adds the method to the list of hooks which are executed
207 after each iteration.
208
209 @param iteration_hook: Method to run after each iteration. A valid
210 hook accepts a single argument which is the
211 test object.
212 """
213 self.after_iteration_hooks.append(iteration_hook)
214
215
jadmanski0afbb632008-06-06 21:10:57 +0000216 def initialize(self):
217 pass
mbligh6231cd62008-02-02 19:18:33 +0000218
219
jadmanski0afbb632008-06-06 21:10:57 +0000220 def setup(self):
221 pass
mbligh6231cd62008-02-02 19:18:33 +0000222
223
mbligh14f98562008-07-29 21:16:27 +0000224 def warmup(self, *args, **dargs):
mbligh4205d892008-07-14 16:23:20 +0000225 pass
mblighb53a3472008-07-11 21:27:58 +0000226
227
mblighb5dac432008-11-27 00:38:44 +0000228 def drop_caches_between_iterations(self):
229 if self.job.drop_caches_between_iterations:
230 print "Dropping caches between iterations"
mbligh53da18e2009-01-05 21:13:26 +0000231 utils.drop_caches()
mblighb5dac432008-11-27 00:38:44 +0000232
233
mblighf58865f2009-05-13 21:32:42 +0000234 def _call_run_once(self, constraints, profile_only,
235 postprocess_profiled_run, args, dargs):
mbligh6b97f792009-03-23 21:23:12 +0000236 self.drop_caches_between_iterations()
mbligh4395bbd2009-03-25 19:34:17 +0000237
238 # execute iteration hooks
mbligh742ae422009-05-13 20:46:41 +0000239 for hook in self.before_iteration_hooks:
240 hook(self)
mblighf58865f2009-05-13 21:32:42 +0000241
242 if profile_only:
243 self.run_once_profiling(postprocess_profiled_run, *args, **dargs)
244 else:
245 self.run_once(*args, **dargs)
246
mbligh742ae422009-05-13 20:46:41 +0000247 for hook in self.after_iteration_hooks:
248 hook(self)
mbligh4395bbd2009-03-25 19:34:17 +0000249
mbligh6b97f792009-03-23 21:23:12 +0000250 self.postprocess_iteration()
mbligh7af09972009-04-17 22:17:08 +0000251 self.analyze_perf_constraints(constraints)
mbligh6b97f792009-03-23 21:23:12 +0000252
253
mbligh4b835b82009-02-11 01:26:13 +0000254 def execute(self, iterations=None, test_length=None, profile_only=False,
mbligha49c5cb2009-02-26 01:01:09 +0000255 _get_time=time.time, postprocess_profiled_run=None,
mbligh7af09972009-04-17 22:17:08 +0000256 constraints=(), *args, **dargs):
mbligh777d96e2008-09-03 16:34:38 +0000257 """
258 This is the basic execute method for the tests inherited from base_test.
259 If you want to implement a benchmark test, it's better to implement
260 the run_once function, to cope with the profiling infrastructure. For
261 other tests, you can just override the default implementation.
mbligh60434712008-07-16 16:35:10 +0000262
mbligh777d96e2008-09-03 16:34:38 +0000263 @param test_length: The minimum test length in seconds. We'll run the
mbligh4b835b82009-02-11 01:26:13 +0000264 run_once function for a number of times large enough to cover the
265 minimum test length.
mbligh777d96e2008-09-03 16:34:38 +0000266
267 @param iterations: A number of iterations that we'll run the run_once
mbligh4b835b82009-02-11 01:26:13 +0000268 function. This parameter is incompatible with test_length and will
269 be silently ignored if you specify both.
270
mblighf58865f2009-05-13 21:32:42 +0000271 @param profile_only: If true run X iterations with profilers enabled.
272 Otherwise run X iterations and one with profiling if profiles are
273 enabled.
mbligh4b835b82009-02-11 01:26:13 +0000274
275 @param _get_time: [time.time] Used for unit test time injection.
mblighc9314082009-02-26 00:48:18 +0000276
mbligha49c5cb2009-02-26 01:01:09 +0000277 @param postprocess_profiled_run: Run the postprocessing for the
278 profiled run.
mbligh777d96e2008-09-03 16:34:38 +0000279 """
280
281 # For our special class of tests, the benchmarks, we don't want
282 # profilers to run during the test iterations. Let's reserve only
283 # the last iteration for profiling, if needed. So let's stop
284 # all profilers if they are present and active.
mblighb3c0c912008-11-27 00:32:45 +0000285 profilers = self.job.profilers
286 if profilers.active():
mbligh777d96e2008-09-03 16:34:38 +0000287 profilers.stop(self)
288 # If the user called this test in an odd way (specified both iterations
mbligh4b835b82009-02-11 01:26:13 +0000289 # and test_length), let's warn them.
mbligh777d96e2008-09-03 16:34:38 +0000290 if iterations and test_length:
mblighf58865f2009-05-13 21:32:42 +0000291 logging.info('Iterations parameter ignored (timed execution).')
mbligh777d96e2008-09-03 16:34:38 +0000292 if test_length:
mbligh4b835b82009-02-11 01:26:13 +0000293 test_start = _get_time()
mbligh777d96e2008-09-03 16:34:38 +0000294 time_elapsed = 0
295 timed_counter = 0
showardb18134f2009-03-20 20:52:18 +0000296 logging.info('Test started. Minimum test length: %d s',
mbligh4b835b82009-02-11 01:26:13 +0000297 test_length)
mbligh777d96e2008-09-03 16:34:38 +0000298 while time_elapsed < test_length:
299 timed_counter = timed_counter + 1
300 if time_elapsed == 0:
showardb18134f2009-03-20 20:52:18 +0000301 logging.info('Executing iteration %d', timed_counter)
mbligh777d96e2008-09-03 16:34:38 +0000302 elif time_elapsed > 0:
showardb18134f2009-03-20 20:52:18 +0000303 logging.info(
mbligh4b835b82009-02-11 01:26:13 +0000304 'Executing iteration %d, time_elapsed %d s',
305 timed_counter, time_elapsed)
mblighf58865f2009-05-13 21:32:42 +0000306 self._call_run_once(constraints, profile_only,
307 postprocess_profiled_run, args, dargs)
mbligh4b835b82009-02-11 01:26:13 +0000308 test_iteration_finish = _get_time()
mbligh777d96e2008-09-03 16:34:38 +0000309 time_elapsed = test_iteration_finish - test_start
showardb18134f2009-03-20 20:52:18 +0000310 logging.info('Test finished after %d iterations',
mbligh4b835b82009-02-11 01:26:13 +0000311 timed_counter)
showardb18134f2009-03-20 20:52:18 +0000312 logging.info('Time elapsed: %d s', time_elapsed)
mbligh777d96e2008-09-03 16:34:38 +0000313 else:
mblighf58865f2009-05-13 21:32:42 +0000314 if iterations is None:
mbligh777d96e2008-09-03 16:34:38 +0000315 iterations = 1
mblighf58865f2009-05-13 21:32:42 +0000316 logging.info('Test started. Number of iterations: %d', iterations)
317 for self.iteration in xrange(1, iterations+1):
318 logging.info('Executing iteration %d of %d', self.iteration,
319 iterations)
320 self._call_run_once(constraints, profile_only,
321 postprocess_profiled_run, args, dargs)
322 logging.info('Test finished after %d iterations.', iterations)
mbligh60434712008-07-16 16:35:10 +0000323
mblighf58865f2009-05-13 21:32:42 +0000324 if not profile_only:
mbligh5e703a22009-06-15 22:00:12 +0000325 self.iteration += 1
mblighf58865f2009-05-13 21:32:42 +0000326 self.run_once_profiling(postprocess_profiled_run, *args, **dargs)
mblighd27604e2009-02-03 02:06:08 +0000327
328 # Do any postprocessing, normally extracting performance keyvals, etc
329 self.postprocess()
mbligh32cb5b42009-05-01 23:05:09 +0000330 self.process_failed_constraints()
mblighd27604e2009-02-03 02:06:08 +0000331
332
mbligha49c5cb2009-02-26 01:01:09 +0000333 def run_once_profiling(self, postprocess_profiled_run, *args, **dargs):
mblighd27604e2009-02-03 02:06:08 +0000334 profilers = self.job.profilers
mbligh60434712008-07-16 16:35:10 +0000335 # Do a profiling run if necessary
mblighb3c0c912008-11-27 00:32:45 +0000336 if profilers.present():
mblighc9314082009-02-26 00:48:18 +0000337 self.drop_caches_between_iterations()
mbligh60434712008-07-16 16:35:10 +0000338 profilers.start(self)
mbligh777d96e2008-09-03 16:34:38 +0000339 print 'Profilers present. Profiling run started'
jadmanski0d390072008-11-19 21:19:56 +0000340 try:
341 self.run_once(*args, **dargs)
mbligha49c5cb2009-02-26 01:01:09 +0000342
343 # Priority to the run_once() argument over the attribute.
344 postprocess_attribute = getattr(self,
345 'postprocess_profiled_run',
346 False)
347
348 if (postprocess_profiled_run or
349 (postprocess_profiled_run is None and
350 postprocess_attribute)):
351 self.postprocess_iteration()
352
jadmanski0d390072008-11-19 21:19:56 +0000353 finally:
354 profilers.stop(self)
355 profilers.report(self)
mbligh60434712008-07-16 16:35:10 +0000356
mbligh60434712008-07-16 16:35:10 +0000357
358 def postprocess(self):
359 pass
360
361
mbligh34b297b2009-02-03 17:49:48 +0000362 def postprocess_iteration(self):
363 pass
364
365
mbligh60434712008-07-16 16:35:10 +0000366 def cleanup(self):
367 pass
mblighcd8a5162008-07-16 16:32:12 +0000368
369
mbligh742ae422009-05-13 20:46:41 +0000370 def _exec(self, args, dargs):
showardee36bc72009-06-18 23:13:53 +0000371 self.job.logging.tee_redirect_debug_dir(self.debugdir,
372 log_name=self.tagged_testname)
jadmanski0afbb632008-06-06 21:10:57 +0000373 try:
jadmanski91d56a92009-04-01 15:20:40 +0000374 if self.network_destabilizing:
375 self.job.disable_warnings("NETWORK")
376
jadmanski62655782008-07-28 21:27:46 +0000377 # write out the test attributes into a keyval
378 dargs = dargs.copy()
jadmanski23afbec2008-09-17 18:12:07 +0000379 run_cleanup = dargs.pop('run_cleanup', self.job.run_test_cleanup)
mbligh234a84f2008-11-20 19:57:43 +0000380 keyvals = dargs.pop('test_attributes', {}).copy()
jadmanski62655782008-07-28 21:27:46 +0000381 keyvals['version'] = self.version
jadmanski2ae0e052008-09-04 16:37:28 +0000382 for i, arg in enumerate(args):
383 keyvals['param-%d' % i] = repr(arg)
384 for name, arg in dargs.iteritems():
385 keyvals['param-%s' % name] = repr(arg)
jadmanski62655782008-07-28 21:27:46 +0000386 self.write_test_keyval(keyvals)
387
mblighcf238192008-07-17 01:18:44 +0000388 _validate_args(args, dargs, self.initialize, self.setup,
389 self.execute, self.cleanup)
mbligh6231cd62008-02-02 19:18:33 +0000390
jadmanski0afbb632008-06-06 21:10:57 +0000391 try:
mblighcf238192008-07-17 01:18:44 +0000392 # Initialize:
mbligh5c1bb252009-03-25 22:06:49 +0000393 _cherry_pick_call(self.initialize, *args, **dargs)
mblighcf238192008-07-17 01:18:44 +0000394
mblighc5ddfd12008-08-04 17:15:00 +0000395 lockfile = open(os.path.join(self.job.tmpdir, '.testlock'), 'w')
396 try:
397 fcntl.flock(lockfile, fcntl.LOCK_EX)
398 # Setup: (compile and install the test, if needed)
399 p_args, p_dargs = _cherry_pick_args(self.setup,args,dargs)
400 utils.update_version(self.srcdir, self.preserve_srcdir,
401 self.version, self.setup,
402 *p_args, **p_dargs)
403 finally:
404 fcntl.flock(lockfile, fcntl.LOCK_UN)
405 lockfile.close()
mblighcf238192008-07-17 01:18:44 +0000406
mblighcf238192008-07-17 01:18:44 +0000407 # Execute:
jadmanski62655782008-07-28 21:27:46 +0000408 os.chdir(self.outputdir)
mbligh4395bbd2009-03-25 19:34:17 +0000409
mbligh5c1bb252009-03-25 22:06:49 +0000410 # call self.warmup cherry picking the arguments it accepts and
411 # translate exceptions if needed
412 _call_test_function(_cherry_pick_call, self.warmup,
413 *args, **dargs)
414
mblighcf238192008-07-17 01:18:44 +0000415 if hasattr(self, 'run_once'):
416 p_args, p_dargs = _cherry_pick_args(self.run_once,
417 args, dargs)
jadmanski886c81f2009-02-19 12:54:03 +0000418 # pull in any non-* and non-** args from self.execute
419 for param in _get_nonstar_args(self.execute):
420 if param in dargs:
421 p_dargs[param] = dargs[param]
mblighcf238192008-07-17 01:18:44 +0000422 else:
423 p_args, p_dargs = _cherry_pick_args(self.execute,
424 args, dargs)
mbligh5c1bb252009-03-25 22:06:49 +0000425
426 _call_test_function(self.execute, *p_args, **p_dargs)
mbligh234a84f2008-11-20 19:57:43 +0000427 except Exception:
428 # Save the exception while we run our cleanup() before
429 # reraising it.
jadmanskid625c7f2008-08-27 14:08:52 +0000430 exc_info = sys.exc_info()
mbligh234a84f2008-11-20 19:57:43 +0000431 try:
432 try:
433 if run_cleanup:
mbligh5c1bb252009-03-25 22:06:49 +0000434 _cherry_pick_call(self.cleanup, *args, **dargs)
mbligh234a84f2008-11-20 19:57:43 +0000435 except Exception:
436 print 'Ignoring exception during cleanup() phase:'
437 traceback.print_exc()
438 print 'Now raising the earlier %s error' % exc_info[0]
mbligh6894ce22009-09-18 19:56:30 +0000439 self.crash_handler_report()
mbligh234a84f2008-11-20 19:57:43 +0000440 finally:
showard75cdfee2009-06-10 17:40:41 +0000441 self.job.logging.restore()
mbligh234a84f2008-11-20 19:57:43 +0000442 try:
443 raise exc_info[0], exc_info[1], exc_info[2]
444 finally:
445 # http://docs.python.org/library/sys.html#sys.exc_info
446 # Be nice and prevent a circular reference.
447 del exc_info
jadmanskid625c7f2008-08-27 14:08:52 +0000448 else:
mbligh234a84f2008-11-20 19:57:43 +0000449 try:
450 if run_cleanup:
mbligh5c1bb252009-03-25 22:06:49 +0000451 _cherry_pick_call(self.cleanup, *args, **dargs)
mbligh6894ce22009-09-18 19:56:30 +0000452 self.crash_handler_report()
mbligh234a84f2008-11-20 19:57:43 +0000453 finally:
showard75cdfee2009-06-10 17:40:41 +0000454 self.job.logging.restore()
jadmanski0afbb632008-06-06 21:10:57 +0000455 except error.AutotestError:
jadmanski91d56a92009-04-01 15:20:40 +0000456 if self.network_destabilizing:
457 self.job.enable_warnings("NETWORK")
mbligh234a84f2008-11-20 19:57:43 +0000458 # Pass already-categorized errors on up.
jadmanski0afbb632008-06-06 21:10:57 +0000459 raise
460 except Exception, e:
jadmanski91d56a92009-04-01 15:20:40 +0000461 if self.network_destabilizing:
462 self.job.enable_warnings("NETWORK")
mbligh234a84f2008-11-20 19:57:43 +0000463 # Anything else is an ERROR in our own code, not execute().
mblighc2180832008-07-25 03:26:12 +0000464 raise error.UnhandledTestError(e)
jadmanski91d56a92009-04-01 15:20:40 +0000465 else:
466 if self.network_destabilizing:
467 self.job.enable_warnings("NETWORK")
mbligh6231cd62008-02-02 19:18:33 +0000468
469
jadmanski886c81f2009-02-19 12:54:03 +0000470def _get_nonstar_args(func):
471 """Extract all the (normal) function parameter names.
472
473 Given a function, returns a tuple of parameter names, specifically
474 excluding the * and ** parameters, if the function accepts them.
475
476 @param func: A callable that we want to chose arguments for.
477
478 @return: A tuple of parameters accepted by the function.
479 """
480 return func.func_code.co_varnames[:func.func_code.co_argcount]
481
482
mblighcf238192008-07-17 01:18:44 +0000483def _cherry_pick_args(func, args, dargs):
mbligh234a84f2008-11-20 19:57:43 +0000484 """Sanitize positional and keyword arguments before calling a function.
485
486 Given a callable (func), an argument tuple and a dictionary of keyword
487 arguments, pick only those arguments which the function is prepared to
488 accept and return a new argument tuple and keyword argument dictionary.
489
490 Args:
491 func: A callable that we want to choose arguments for.
492 args: A tuple of positional arguments to consider passing to func.
493 dargs: A dictionary of keyword arguments to consider passing to func.
494 Returns:
495 A tuple of: (args tuple, keyword arguments dictionary)
496 """
mblighcf238192008-07-17 01:18:44 +0000497 # Cherry pick args:
498 if func.func_code.co_flags & 0x04:
499 # func accepts *args, so return the entire args.
500 p_args = args
501 else:
502 p_args = ()
503
504 # Cherry pick dargs:
505 if func.func_code.co_flags & 0x08:
506 # func accepts **dargs, so return the entire dargs.
507 p_dargs = dargs
508 else:
mbligh234a84f2008-11-20 19:57:43 +0000509 # Only return the keyword arguments that func accepts.
mblighcf238192008-07-17 01:18:44 +0000510 p_dargs = {}
jadmanski886c81f2009-02-19 12:54:03 +0000511 for param in _get_nonstar_args(func):
mblighcf238192008-07-17 01:18:44 +0000512 if param in dargs:
513 p_dargs[param] = dargs[param]
514
515 return p_args, p_dargs
516
517
mbligh5c1bb252009-03-25 22:06:49 +0000518def _cherry_pick_call(func, *args, **dargs):
519 """Cherry picks arguments from args/dargs based on what "func" accepts
520 and calls the function with the picked arguments."""
521 p_args, p_dargs = _cherry_pick_args(func, args, dargs)
522 return func(*p_args, **p_dargs)
523
524
mblighcf238192008-07-17 01:18:44 +0000525def _validate_args(args, dargs, *funcs):
mbligh234a84f2008-11-20 19:57:43 +0000526 """Verify that arguments are appropriate for at least one callable.
527
528 Given a list of callables as additional parameters, verify that
529 the proposed keyword arguments in dargs will each be accepted by at least
530 one of the callables.
531
532 NOTE: args is currently not supported and must be empty.
533
534 Args:
535 args: A tuple of proposed positional arguments.
536 dargs: A dictionary of proposed keyword arguments.
537 *funcs: Callables to be searched for acceptance of args and dargs.
538 Raises:
539 error.AutotestError: if an arg won't be accepted by any of *funcs.
540 """
mblighcf238192008-07-17 01:18:44 +0000541 all_co_flags = 0
542 all_varnames = ()
543 for func in funcs:
544 all_co_flags |= func.func_code.co_flags
545 all_varnames += func.func_code.co_varnames[:func.func_code.co_argcount]
546
547 # Check if given args belongs to at least one of the methods below.
548 if len(args) > 0:
549 # Current implementation doesn't allow the use of args.
mbligh234a84f2008-11-20 19:57:43 +0000550 raise error.TestError('Unnamed arguments not accepted. Please '
551 'call job.run_test with named args only')
mblighcf238192008-07-17 01:18:44 +0000552
553 # Check if given dargs belongs to at least one of the methods below.
554 if len(dargs) > 0:
555 if not all_co_flags & 0x08:
556 # no func accepts *dargs, so:
557 for param in dargs:
558 if not param in all_varnames:
559 raise error.AutotestError('Unknown parameter: %s' % param)
560
561
mbligh6231cd62008-02-02 19:18:33 +0000562def _installtest(job, url):
mblighc5ddfd12008-08-04 17:15:00 +0000563 (group, name) = job.pkgmgr.get_package_name(url, 'test')
mbligh6231cd62008-02-02 19:18:33 +0000564
jadmanski0afbb632008-06-06 21:10:57 +0000565 # Bail if the test is already installed
566 group_dir = os.path.join(job.testdir, "download", group)
567 if os.path.exists(os.path.join(group_dir, name)):
568 return (group, name)
mbligh6231cd62008-02-02 19:18:33 +0000569
jadmanski0afbb632008-06-06 21:10:57 +0000570 # If the group directory is missing create it and add
571 # an empty __init__.py so that sub-directories are
572 # considered for import.
573 if not os.path.exists(group_dir):
574 os.mkdir(group_dir)
575 f = file(os.path.join(group_dir, '__init__.py'), 'w+')
576 f.close()
mbligh6231cd62008-02-02 19:18:33 +0000577
jadmanski0afbb632008-06-06 21:10:57 +0000578 print name + ": installing test url=" + url
mblighc5ddfd12008-08-04 17:15:00 +0000579 tarball = os.path.basename(url)
580 tarball_path = os.path.join(group_dir, tarball)
581 test_dir = os.path.join(group_dir, name)
582 job.pkgmgr.fetch_pkg(tarball, tarball_path,
583 repo_url = os.path.dirname(url))
584
585 # Create the directory for the test
586 if not os.path.exists(test_dir):
587 os.mkdir(os.path.join(group_dir, name))
588
589 job.pkgmgr.untar_pkg(tarball_path, test_dir)
590
591 os.remove(tarball_path)
mbligh6231cd62008-02-02 19:18:33 +0000592
jadmanski0afbb632008-06-06 21:10:57 +0000593 # For this 'sub-object' to be importable via the name
594 # 'group.name' we need to provide an __init__.py,
595 # so link the main entry point to this.
596 os.symlink(name + '.py', os.path.join(group_dir, name,
597 '__init__.py'))
mbligh6231cd62008-02-02 19:18:33 +0000598
jadmanski0afbb632008-06-06 21:10:57 +0000599 # The test is now installed.
600 return (group, name)
mbligh6231cd62008-02-02 19:18:33 +0000601
602
mbligh5c1bb252009-03-25 22:06:49 +0000603def _call_test_function(func, *args, **dargs):
604 """Calls a test function and translates exceptions so that errors
605 inside test code are considered test failures."""
606 try:
607 return func(*args, **dargs)
608 except error.AutotestError:
609 # Pass already-categorized errors on up as is.
610 raise
611 except Exception, e:
612 # Other exceptions must be treated as a FAIL when
613 # raised during the test functions
614 raise error.UnhandledTestFail(e)
615
616
mbligh6231cd62008-02-02 19:18:33 +0000617def runtest(job, url, tag, args, dargs,
jadmanski30e9b592008-09-25 19:51:57 +0000618 local_namespace={}, global_namespace={},
mbligh4395bbd2009-03-25 19:34:17 +0000619 before_test_hook=None, after_test_hook=None,
620 before_iteration_hook=None, after_iteration_hook=None):
jadmanski0afbb632008-06-06 21:10:57 +0000621 local_namespace = local_namespace.copy()
622 global_namespace = global_namespace.copy()
mbligh6231cd62008-02-02 19:18:33 +0000623
jadmanski0afbb632008-06-06 21:10:57 +0000624 # if this is not a plain test name then download and install the
625 # specified test
mblighc5ddfd12008-08-04 17:15:00 +0000626 if url.endswith('.tar.bz2'):
jadmanski0afbb632008-06-06 21:10:57 +0000627 (group, testname) = _installtest(job, url)
628 bindir = os.path.join(job.testdir, 'download', group, testname)
629 site_bindir = None
630 else:
631 # if the test is local, it can be found in either testdir
632 # or site_testdir. tests in site_testdir override tests
633 # defined in testdir
634 (group, testname) = ('', url)
635 bindir = os.path.join(job.testdir, group, testname)
636 if hasattr(job, 'site_testdir'):
637 site_bindir = os.path.join(job.site_testdir,
638 group, testname)
639 else:
640 site_bindir = None
mbligh6231cd62008-02-02 19:18:33 +0000641
mblighc5ddfd12008-08-04 17:15:00 +0000642 # The job object here can be that of a server side job or a client
643 # side job. 'install_pkg' method won't be present for server side
644 # jobs, so do the fetch only if that method is present in the job
645 # obj.
646 if hasattr(job, 'install_pkg'):
647 try:
648 job.install_pkg(testname, 'test', bindir)
jadmanskic27c2312009-08-05 20:58:51 +0000649 except error.PackageInstallError, e:
mblighc5ddfd12008-08-04 17:15:00 +0000650 # continue as a fall back mechanism and see if the test code
651 # already exists on the machine
652 pass
653
jadmanski0afbb632008-06-06 21:10:57 +0000654 outputdir = os.path.join(job.resultdir, testname)
655 if tag:
656 outputdir += '.' + tag
mbligh6231cd62008-02-02 19:18:33 +0000657
jadmanski0afbb632008-06-06 21:10:57 +0000658 # if we can find the test in site_bindir, use this version
659 if site_bindir and os.path.exists(site_bindir):
660 bindir = site_bindir
661 testdir = job.site_testdir
662 elif os.path.exists(bindir):
663 testdir = job.testdir
mblighc5ddfd12008-08-04 17:15:00 +0000664 else:
jadmanski0afbb632008-06-06 21:10:57 +0000665 raise error.TestError(testname + ': test does not exist')
mbligh6231cd62008-02-02 19:18:33 +0000666
mblighc5ddfd12008-08-04 17:15:00 +0000667 local_namespace['job'] = job
668 local_namespace['bindir'] = bindir
669 local_namespace['outputdir'] = outputdir
670
jadmanski0afbb632008-06-06 21:10:57 +0000671 if group:
672 sys.path.insert(0, os.path.join(testdir, 'download'))
673 group += '.'
674 else:
675 sys.path.insert(0, os.path.join(testdir, testname))
mbligh6231cd62008-02-02 19:18:33 +0000676
jadmanski0afbb632008-06-06 21:10:57 +0000677 try:
jadmanski0afbb632008-06-06 21:10:57 +0000678 exec ("import %s%s" % (group, testname),
679 local_namespace, global_namespace)
680 exec ("mytest = %s%s.%s(job, bindir, outputdir)" %
681 (group, testname, testname),
682 local_namespace, global_namespace)
683 finally:
jadmanski0afbb632008-06-06 21:10:57 +0000684 sys.path.pop(0)
mbligh6231cd62008-02-02 19:18:33 +0000685
jadmanski0afbb632008-06-06 21:10:57 +0000686 pwd = os.getcwd()
687 os.chdir(outputdir)
mbligh4395bbd2009-03-25 19:34:17 +0000688
jadmanski0afbb632008-06-06 21:10:57 +0000689 try:
690 mytest = global_namespace['mytest']
jadmanski30e9b592008-09-25 19:51:57 +0000691 if before_test_hook:
692 before_test_hook(mytest)
mbligh742ae422009-05-13 20:46:41 +0000693
694 # we use the register iteration hooks methods to register the passed
695 # in hooks
696 if before_iteration_hook:
697 mytest.register_before_iteration_hook(before_iteration_hook)
698 if after_iteration_hook:
699 mytest.register_after_iteration_hook(after_iteration_hook)
700 mytest._exec(args, dargs)
jadmanski0afbb632008-06-06 21:10:57 +0000701 finally:
jadmanski213b02b2008-08-26 20:51:58 +0000702 os.chdir(pwd)
jadmanski0afbb632008-06-06 21:10:57 +0000703 if after_test_hook:
704 after_test_hook(mytest)
jadmanski825e24c2008-08-27 20:54:31 +0000705 shutil.rmtree(mytest.tmpdir, ignore_errors=True)