def test(self): """ Create the Robot command and execute it. """ suite_name, test_name = self._filename.split(':')[1].split('.') log_stdout = output.LoggingFile(loggers=[self.log], level=logging.INFO) log_stderr = output.LoggingFile(loggers=[self.log], level=logging.ERROR) result = run(self.filename, suite=suite_name, test=test_name, outputdir=self.outputdir, stdout=log_stdout, stderr=log_stderr) if result: self.fail('Robot execution returned a ' 'non-0 exit code (%s)' % result)
def run_test(self, test_factory, queue): """ Run a test instance in a subprocess. :param instance: Test instance. :type instance: :class:`avocado.test.Test` instance. :param queue: Multiprocess queue. :type queue: :class`multiprocessing.Queue` instance. """ def timeout_handler(signum, frame): e_msg = "Timeout reached waiting for %s to end" % instance raise exceptions.TestTimeoutError(e_msg) def interrupt_handler(signum, frame): e_msg = "Test %s interrupted by user" % instance raise exceptions.TestInterruptedError(e_msg) sys.stdout = output.LoggingFile( logger=logging.getLogger('avocado.test.stdout')) sys.stderr = output.LoggingFile( logger=logging.getLogger('avocado.test.stderr')) try: instance = self.job.test_loader.load_test(test_factory) if instance.runner_queue is None: instance.runner_queue = queue runtime.CURRENT_TEST = instance early_state = instance.get_state() queue.put(early_state) except Exception: exc_info = sys.exc_info() app_logger = logging.getLogger('avocado.app') app_logger.exception('Exception loading test') tb_info = stacktrace.tb_info(exc_info) queue.put({'load_exception': tb_info}) return signal.signal(signal.SIGUSR1, timeout_handler) signal.signal(signal.SIGINT, interrupt_handler) self.result.start_test(early_state) try: instance.run_avocado() finally: queue.put(instance.get_state())
def run_suite(self, test_suite, variants, timeout=0, replay_map=None, suite_order="variants-per-test"): """ Run one or more tests and report with test result. :param params_list: a list of param dicts. :param variants: A varianter iterator (unused here) :return: a set with types of test failures. """ del test_suite # using self.job.references instead del variants # we're not using multiplexation here if suite_order != "variants-per-test" and suite_order is not None: raise exceptions.JobError("execution-order %s is not supported " "for remote execution." % suite_order) del suite_order # suite_order is ignored for now if not timeout: # avoid timeout = 0 timeout = None summary = set() stdout_backup = sys.stdout stderr_backup = sys.stderr fabric_debugfile = os.path.join(self.job.logdir, 'remote.log') paramiko_logger = logging.getLogger('paramiko') fabric_logger = logging.getLogger('avocado.fabric') remote_logger = logging.getLogger('avocado.remote') app_logger = logging.getLogger('avocado.debug') fmt = ('%(asctime)s %(module)-10.10s L%(lineno)-.4d %(' 'levelname)-5.5s| %(message)s') formatter = logging.Formatter(fmt=fmt, datefmt='%H:%M:%S') file_handler = logging.FileHandler(filename=fabric_debugfile) file_handler.setFormatter(formatter) fabric_logger.addHandler(file_handler) paramiko_logger.addHandler(file_handler) remote_logger.addHandler(file_handler) if self.job.args.show_job_log: output.add_log_handler(paramiko_logger.name) logger_list = [output.LOG_JOB] sys.stdout = output.LoggingFile(loggers=logger_list) sys.stderr = output.LoggingFile(loggers=logger_list) try: try: self.setup() avocado_installed, _ = self.check_remote_avocado() if not avocado_installed: raise exceptions.JobError('Remote machine does not seem to' ' have avocado installed') except Exception as details: stacktrace.log_exc_info(sys.exc_info(), logger=LOG_JOB) raise exceptions.JobError(details) results = self.run_test(self.job.references, timeout) remote_log_dir = os.path.dirname(results['debuglog']) self.result.tests_total = results['total'] local_log_dir = self.job.logdir for tst in results['tests']: name = tst['test'].split('-', 1) name = [name[0]] + name[1].split(';') if len(name) == 3: name[2] = {"variant_id": name[2]} name = TestID(*name, no_digits=-1) state = dict(name=name, time_elapsed=tst['time'], time_start=tst['start'], time_end=tst['end'], status=tst['status'], logdir=tst['logdir'], logfile=tst['logfile'], fail_reason=tst['fail_reason'], job_logdir=local_log_dir, job_unique_id='') self.result.start_test(state) self.job._result_events_dispatcher.map_method( 'start_test', self.result, state) self.result.check_test(state) self.job._result_events_dispatcher.map_method( 'end_test', self.result, state) if state['status'] == "INTERRUPTED": summary.add("INTERRUPTED") elif not status.mapping[state['status']]: summary.add("FAIL") zip_filename = remote_log_dir + '.zip' zip_path_filename = os.path.join(local_log_dir, os.path.basename(zip_filename)) self.remote.receive_files(local_log_dir, zip_filename) archive.uncompress(zip_path_filename, local_log_dir) os.remove(zip_path_filename) self.result.end_tests() self.job._result_events_dispatcher.map_method( 'post_tests', self.job) finally: try: self.tear_down() except Exception as details: stacktrace.log_exc_info(sys.exc_info(), logger=LOG_JOB) raise exceptions.JobError(details) sys.stdout = stdout_backup sys.stderr = stderr_backup return summary
def _run_test(job, result, test_factory, queue): """ Run a test instance. This code is the first thing that runs inside a new process, known here as the test process. It communicates to the test runner by using :param:`queue`. It's important that this early state is given to the test runner in a reliable way. :param test_factory: Test factory (test class and parameters). :type test_factory: tuple of :class:`avocado.core.test.Test` and dict. :param queue: Multiprocess queue. :type queue: :class:`multiprocessing.Queue` instance. """ sys.stdout = output.LoggingFile(["[stdout] "], loggers=[TEST_LOG]) sys.stderr = output.LoggingFile(["[stderr] "], loggers=[TEST_LOG]) def sigterm_handler(signum, frame): # pylint: disable=W0613 """ Produce traceback on SIGTERM """ raise RuntimeError("Test interrupted by SIGTERM") signal.signal(signal.SIGTERM, sigterm_handler) # At this point, the original `sys.stdin` has already been # closed and replaced with `os.devnull` by # `multiprocessing.Process()` (not directly from Avocado # code). Still, tests trying to use file descriptor 0 would # be able to read from the tty, and would hang. Let's replace # STDIN fd (0), with the same fd previously set by # `multiprocessing.Process()` os.dup2(sys.stdin.fileno(), 0) instance = loader.load_test(test_factory) if instance.runner_queue is None: instance.set_runner_queue(queue) early_state = instance.get_state() early_state['early_status'] = True try: queue.put(early_state) except queueFullException: instance.error(stacktrace.str_unpickable_object(early_state)) result.start_test(early_state) job.result_events_dispatcher.map_method('start_test', result, early_state) if job.config.get('run.log_test_data_directories'): data_sources = getattr(instance, "DATA_SOURCES", []) if data_sources: locations = [] for source in data_sources: locations.append(instance.get_data("", source=source, must_exist=False)) TEST_LOG.info('Test data directories: ') for source, location in zip(data_sources, locations): if location is not None: TEST_LOG.info(' %s: %s', source, location) TEST_LOG.info('') try: instance.run_avocado() finally: try: state = instance.get_state() queue.put(state) except queueFullException: instance.error(stacktrace.str_unpickable_object(state))