def run_group(self, function, tag=None, **dargs): """ Run a function nested within a group level. function: Callable to run. tag: An optional tag name for the group. If None (default) function.__name__ will be used. **dargs: Named arguments for the function. """ if tag: name = tag else: name = function.__name__ try: return self._rungroup(subdir=None, testname=name, function=function, timeout=None, **dargs) except (SystemExit, error.TestBaseException): raise # If there was a different exception, turn it into a TestError. # It will be caught by step_engine or _run_step_fn. except Exception, e: raise error.UnhandledTestError(e)
def test_run_test_logs_non_test_error_from_unhandled_error(self): self.construct_job(True) # set up stubs self.god.stub_function(self.job.pkgmgr, 'get_package_name') self.god.stub_function(self.job, "_runtest") # create an unhandled error object class MyError(Exception): pass real_error = MyError("this is the real error message") unhandled_error = error.UnhandledTestError(real_error) reason = first_line_comparator("Unhandled MyError: %s" % real_error) # set up the recording testname = "error_test" outputdir = os.path.join(self.job.resultdir, testname) self.job.pkgmgr.get_package_name.expect_call( testname, 'test').and_return(("", testname)) os.path.exists.expect_call(outputdir).and_return(False) self.job.record.expect_call("START", testname, testname, optional_fields=None) self.job._runtest.expect_call(testname, "", None, (), {}).and_raises( unhandled_error) self.job.record.expect_call("ERROR", testname, testname, reason) self.job.record.expect_call("END ERROR", testname, testname) self.job.harness.run_test_complete.expect_call() utils.drop_caches.expect_call() # run and check self.job.run_test(testname) self.god.check_playback()
def _runtest(self, url, tag, timeout, args, dargs): try: l = lambda: test.runtest(self, url, tag, args, dargs) pid = parallel.fork_start(self.resultdir, l) self._forkwait(pid, timeout) except error.TestBaseException: # These are already classified with an error type (exit_status) raise except error.JobError: raise # Caught further up and turned into an ABORT. except Exception, e: # Converts all other exceptions thrown by the test regardless # of phase into a TestError(TestBaseException) subclass that # reports them with their full stack trace. raise error.UnhandledTestError(e)
def fork_start(tmp, l): sys.stdout.flush() sys.stderr.flush() pid = os.fork() if pid: # Parent return pid try: try: l() except error.AutotestError: raise except Exception, e: raise error.UnhandledTestError(e) except Exception, detail: try: try: logging.error('child process failed') # logging.exception() uses ERROR level, but we want DEBUG for # the traceback for line in traceback.format_exc().splitlines(): logging.debug(line) finally: # note that exceptions originating in this block won't make it # to the logs output_dir = os.path.join(tmp, 'debug') if not os.path.exists(output_dir): os.makedirs(output_dir) ename = os.path.join(output_dir, "error-%d" % os.getpid()) pickle.dump(detail, open(ename, "w")) sys.stdout.flush() sys.stderr.flush() finally: # clear exception information to allow garbage collection of # objects referenced by the exception's traceback sys.exc_clear() gc.collect() os._exit(1)
def _exec(self, args, dargs): self.job.logging.tee_redirect_debug_dir(self.debugdir, log_name=self.tagged_testname) try: if self.network_destabilizing: self.job.disable_warnings("NETWORK") # write out the test attributes into a keyval dargs = dargs.copy() run_cleanup = dargs.pop('run_cleanup', self.job.run_test_cleanup) keyvals = dargs.pop('test_attributes', {}).copy() keyvals['version'] = self.version for i, arg in enumerate(args): keyvals['param-%d' % i] = repr(arg) for name, arg in dargs.iteritems(): keyvals['param-%s' % name] = repr(arg) self.write_test_keyval(keyvals) _validate_args(args, dargs, self.initialize, self.setup, self.execute, self.cleanup) try: # Make resultsdir and tmpdir accessible to everyone. We may # output data to these directories as others, e.g., chronos. self._make_writable_to_others(self.tmpdir) self._make_writable_to_others(self.resultsdir) # Initialize: _cherry_pick_call(self.initialize, *args, **dargs) lockfile = open(os.path.join(self.job.tmpdir, '.testlock'), 'w') try: fcntl.flock(lockfile, fcntl.LOCK_EX) # Setup: (compile and install the test, if needed) p_args, p_dargs = _cherry_pick_args( self.setup, args, dargs) utils.update_version(self.srcdir, self.preserve_srcdir, self.version, self.setup, *p_args, **p_dargs) finally: fcntl.flock(lockfile, fcntl.LOCK_UN) lockfile.close() # Execute: os.chdir(self.outputdir) # call self.warmup cherry picking the arguments it accepts and # translate exceptions if needed _call_test_function(_cherry_pick_call, self.warmup, *args, **dargs) if hasattr(self, 'run_once'): p_args, p_dargs = _cherry_pick_args( self.run_once, args, dargs) # pull in any non-* and non-** args from self.execute for param in _get_nonstar_args(self.execute): if param in dargs: p_dargs[param] = dargs[param] else: p_args, p_dargs = _cherry_pick_args( self.execute, args, dargs) _call_test_function(self.execute, *p_args, **p_dargs) except Exception: # Save the exception while we run our cleanup() before # reraising it, but log it to so actual time of error is known. exc_info = sys.exc_info() logging.warning('The test failed with the following exception', exc_info=True) try: try: if run_cleanup: logging.debug('Running cleanup for test.') _cherry_pick_call(self.cleanup, *args, **dargs) except Exception: logging.error('Ignoring exception during cleanup() ' 'phase:') traceback.print_exc() logging.error('Now raising the earlier %s error', exc_info[0]) self.crash_handler_report() finally: # Raise exception after running cleanup, reporting crash, # and restoring job's logging, even if the first two # actions fail. self.job.logging.restore() try: raise exc_info[0], exc_info[1], exc_info[2] finally: # http://docs.python.org/library/sys.html#sys.exc_info # Be nice and prevent a circular reference. del exc_info else: try: if run_cleanup: _cherry_pick_call(self.cleanup, *args, **dargs) self.crash_handler_report() finally: self.job.logging.restore() except error.AutotestError: if self.network_destabilizing: self.job.enable_warnings("NETWORK") # Pass already-categorized errors on up. raise except Exception, e: if self.network_destabilizing: self.job.enable_warnings("NETWORK") # Anything else is an ERROR in our own code, not execute(). raise error.UnhandledTestError(e)
def _exec(self, args, dargs): self.job.logging.tee_redirect_debug_dir(self.debugdir, log_name=self.tagged_testname) try: if self.network_destabilizing: self.job.disable_warnings("NETWORK") # write out the test attributes into a keyval dargs = dargs.copy() run_cleanup = dargs.pop('run_cleanup', self.job.run_test_cleanup) keyvals = dargs.pop('test_attributes', {}).copy() keyvals['version'] = self.version for i, arg in enumerate(args): keyvals['param-%d' % i] = repr(arg) for name, arg in dargs.iteritems(): keyvals['param-%s' % name] = repr(arg) self.write_test_keyval(keyvals) _validate_args(args, dargs, self.initialize, self.setup, self.execute, self.cleanup) try: # Initialize: _cherry_pick_call(self.initialize, *args, **dargs) lockfile = open(os.path.join(self.job.tmpdir, '.testlock'), 'w') try: fcntl.flock(lockfile, fcntl.LOCK_EX) # Setup: (compile and install the test, if needed) p_args, p_dargs = _cherry_pick_args( self.setup, args, dargs) utils.update_version(self.srcdir, self.preserve_srcdir, self.version, self.setup, *p_args, **p_dargs) finally: fcntl.flock(lockfile, fcntl.LOCK_UN) lockfile.close() # Execute: os.chdir(self.outputdir) # call self.warmup cherry picking the arguments it accepts and # translate exceptions if needed _call_test_function(_cherry_pick_call, self.warmup, *args, **dargs) if hasattr(self, 'run_once'): p_args, p_dargs = _cherry_pick_args( self.run_once, args, dargs) # pull in any non-* and non-** args from self.execute for param in _get_nonstar_args(self.execute): if param in dargs: p_dargs[param] = dargs[param] else: p_args, p_dargs = _cherry_pick_args( self.execute, args, dargs) _call_test_function(self.execute, *p_args, **p_dargs) except Exception: try: logging.exception('Exception escaping from test:') except: pass # don't let logging exceptions here interfere # Save the exception while we run our cleanup() before # reraising it. exc_info = sys.exc_info() try: try: if run_cleanup: _cherry_pick_call(self.cleanup, *args, **dargs) except Exception: print 'Ignoring exception during cleanup() phase:' traceback.print_exc() print 'Now raising the earlier %s error' % exc_info[0] self.crash_handler_report() finally: self.job.logging.restore() try: raise exc_info[0], exc_info[1], exc_info[2] finally: # http://docs.python.org/library/sys.html#sys.exc_info # Be nice and prevent a circular reference. del exc_info else: try: if run_cleanup: _cherry_pick_call(self.cleanup, *args, **dargs) self.crash_handler_report() finally: self.job.logging.restore() except error.AutotestError: if self.network_destabilizing: self.job.enable_warnings("NETWORK") # Pass already-categorized errors on up. raise except Exception, e: if self.network_destabilizing: self.job.enable_warnings("NETWORK") # Anything else is an ERROR in our own code, not execute(). raise error.UnhandledTestError(e)