class Runner(SuiteVisitor): def __init__(self, output, settings): self.result = None self._output = output self._settings = settings self._variables = VariableScopes(settings) self._suite = None self._suite_status = None self._executed_tests = None @property def _context(self): return EXECUTION_CONTEXTS.current def start_suite(self, suite): result = TestSuite(source=suite.source, name=suite.name, doc=suite.doc, metadata=suite.metadata, starttime=get_timestamp()) if not self.result: result.set_criticality(self._settings.critical_tags, self._settings.non_critical_tags) self.result = Result(root_suite=result) self.result.configure(status_rc=self._settings.status_rc, stat_config=self._settings.statistics_config) else: self._suite.suites.append(result) self._suite = result self._suite_status = SuiteStatus(self._suite_status, self._settings.exit_on_failure, self._settings.exit_on_error, self._settings.skip_teardown_on_exit) ns = Namespace(self._variables, result, suite.resource.keywords, suite.resource.imports) ns.start_suite() ns.variables.set_from_variable_table(suite.resource.variables) EXECUTION_CONTEXTS.start_suite(ns, self._output, self._settings.dry_run) self._context.set_suite_variables(result) if not self._suite_status.failures: ns.handle_imports() ns.variables.resolve_delayed() result.doc = self._resolve_setting(result.doc) result.metadata = [(self._resolve_setting(n), self._resolve_setting(v)) for n, v in result.metadata.items()] self._context.set_suite_variables(result) self._output.start_suite(ModelCombiner(result, suite, tests=suite.tests, suites=suite.suites, test_count=suite.test_count)) self._output.register_error_listener(self._suite_status.error_occurred) self._run_setup(suite.keywords.setup, self._suite_status) self._executed_tests = NormalizedDict(ignore='_') def _resolve_setting(self, value): return self._variables.replace_string(value, ignore_errors=True) def end_suite(self, suite): self._suite.message = self._suite_status.message self._context.report_suite_status(self._suite.status, self._suite.full_message) with self._context.suite_teardown(): failure = self._run_teardown(suite.keywords.teardown, self._suite_status) if failure: self._suite.suite_teardown_failed(unicode(failure)) if self._suite.statistics.critical.failed: self._suite_status.critical_failure_occurred() self._suite.endtime = get_timestamp() self._suite.message = self._suite_status.message self._context.end_suite(self._suite) self._suite = self._suite.parent self._suite_status = self._suite_status.parent def visit_test(self, test): if test.name in self._executed_tests: self._output.warn("Multiple test cases with name '%s' executed in " "test suite '%s'." % (test.name, self._suite.longname)) self._executed_tests[test.name] = True result = self._suite.tests.create(name=test.name, doc=self._resolve_setting(test.doc), tags=test.tags, starttime=get_timestamp(), timeout=self._get_timeout(test)) status = TestStatus(self._suite_status, result.critical) if not status.failures and not test.name: status.test_failed('Test case name cannot be empty.') if not status.failures and not test.keywords.normal: status.test_failed('Test case contains no keywords.') try: result.tags = self._context.variables.replace_list(result.tags) except DataError as err: status.test_failed('Replacing variables from test tags failed: %s' % unicode(err)) self._context.start_test(result) self._output.start_test(ModelCombiner(result, test)) if status.exit: self._add_exit_combine() result.tags.add('robot-exit') self._run_setup(test.keywords.setup, status, result) try: if not status.failures: runner = KeywordRunner(self._context, bool(test.template)) runner.run_keywords(test.keywords.normal) else: status.test_failed(status.message) except PassExecution as exception: err = exception.earlier_failures if err: status.test_failed(err) else: result.message = exception.message except ExecutionFailed as err: status.test_failed(err) result.status = status.status result.message = status.message or result.message if status.teardown_allowed: with self._context.test_teardown(result): failure = self._run_teardown(test.keywords.teardown, status, result) if failure and result.critical: status.critical_failure_occurred() if not status.failures and result.timeout and result.timeout.timed_out(): status.test_failed(result.timeout.get_message()) result.message = status.message result.status = status.status result.endtime = get_timestamp() self._output.end_test(ModelCombiner(result, test)) self._context.end_test(result) def _add_exit_combine(self): exit_combine = ('NOT robot-exit', '') if exit_combine not in self._settings['TagStatCombine']: self._settings['TagStatCombine'].append(exit_combine) def _get_timeout(self, test): if not test.timeout: return None timeout = TestTimeout(test.timeout.value, test.timeout.message, self._variables) timeout.start() return timeout def _run_setup(self, setup, status, result=None): if not status.failures: exception = self._run_setup_or_teardown(setup) status.setup_executed(exception) if result and isinstance(exception, PassExecution): result.message = exception.message def _run_teardown(self, teardown, status, result=None): if status.teardown_allowed: exception = self._run_setup_or_teardown(teardown) status.teardown_executed(exception) failed = not isinstance(exception, PassExecution) if result and exception: result.message = status.message if failed else exception.message return exception if failed else None def _run_setup_or_teardown(self, data): if not data: return None try: name = self._variables.replace_string(data.name) except DataError as err: return err if name.upper() in ('', 'NONE'): return None runner = KeywordRunner(self._context) try: runner.run_keyword(data, name=name) except ExecutionFailed as err: return err
class Runner(SuiteVisitor): def __init__(self, output, settings): self.result = None self._output = output self._settings = settings self._variables = VariableScopes(settings) self._suite = None self._suite_status = None self._executed_tests = None @property def _context(self): return EXECUTION_CONTEXTS.current def start_suite(self, suite): self._output.library_listeners.new_suite_scope() result = TestSuite(source=suite.source, name=suite.name, doc=suite.doc, metadata=suite.metadata, starttime=get_timestamp(), rpa=self._settings.rpa) if not self.result: result.set_criticality(self._settings.critical_tags, self._settings.non_critical_tags) self.result = Result(root_suite=result, rpa=self._settings.rpa) self.result.configure(status_rc=self._settings.status_rc, stat_config=self._settings.statistics_config) else: self._suite.suites.append(result) self._suite = result self._suite_status = SuiteStatus(self._suite_status, self._settings.exit_on_failure, self._settings.exit_on_error, self._settings.skip_teardown_on_exit) ns = Namespace(self._variables, result, suite.resource) ns.start_suite() ns.variables.set_from_variable_table(suite.resource.variables) EXECUTION_CONTEXTS.start_suite(result, ns, self._output, self._settings.dry_run) self._context.set_suite_variables(result) if not self._suite_status.failures: ns.handle_imports() ns.variables.resolve_delayed() result.doc = self._resolve_setting(result.doc) result.metadata = [(self._resolve_setting(n), self._resolve_setting(v)) for n, v in result.metadata.items()] self._context.set_suite_variables(result) self._output.start_suite(ModelCombiner(suite, result, tests=suite.tests, suites=suite.suites, test_count=suite.test_count)) self._output.register_error_listener(self._suite_status.error_occurred) self._run_setup(suite.keywords.setup, self._suite_status) self._executed_tests = NormalizedDict(ignore='_') def _resolve_setting(self, value): if is_list_like(value): return self._variables.replace_list(value, ignore_errors=True) return self._variables.replace_string(value, ignore_errors=True) def end_suite(self, suite): self._suite.message = self._suite_status.message self._context.report_suite_status(self._suite.status, self._suite.full_message) with self._context.suite_teardown(): failure = self._run_teardown(suite.keywords.teardown, self._suite_status) if failure: self._suite.suite_teardown_failed(unic(failure)) if self._suite.statistics.critical.failed: self._suite_status.critical_failure_occurred() self._suite.endtime = get_timestamp() self._suite.message = self._suite_status.message self._context.end_suite(ModelCombiner(suite, self._suite)) self._suite = self._suite.parent self._suite_status = self._suite_status.parent self._output.library_listeners.discard_suite_scope() def visit_test(self, test): if test.name in self._executed_tests: self._output.warn("Multiple test cases with name '%s' executed in " "test suite '%s'." % (test.name, self._suite.longname)) self._executed_tests[test.name] = True result = self._suite.tests.create(name=test.name, doc=self._resolve_setting(test.doc), tags=self._resolve_setting(test.tags), starttime=get_timestamp(), timeout=self._get_timeout(test)) self._context.start_test(result) self._output.start_test(ModelCombiner(test, result)) status = TestStatus(self._suite_status, result.critical) if status.exit: self._add_exit_combine() result.tags.add('robot:exit') if not status.failures and not test.name: status.test_failed('Test case name cannot be empty.') if not status.failures and not test.keywords.normal: status.test_failed('Test case contains no keywords.') self._run_setup(test.keywords.setup, status, result) try: if not status.failures: StepRunner(self._context, test.template).run_steps(test.keywords.normal) else: status.test_failed(status.message) except PassExecution as exception: err = exception.earlier_failures if err: status.test_failed(err) else: result.message = exception.message except ExecutionStatus as err: status.test_failed(err) result.status = status.status result.message = status.message or result.message if status.teardown_allowed: with self._context.test_teardown(result): failure = self._run_teardown(test.keywords.teardown, status, result) if failure and result.critical: status.critical_failure_occurred() if not status.failures and result.timeout and result.timeout.timed_out(): status.test_failed(result.timeout.get_message()) result.message = status.message result.status = status.status result.endtime = get_timestamp() self._output.end_test(ModelCombiner(test, result)) self._context.end_test(result) def _add_exit_combine(self): exit_combine = ('NOT robot:exit', '') if exit_combine not in self._settings['TagStatCombine']: self._settings['TagStatCombine'].append(exit_combine) def _get_timeout(self, test): if not test.timeout: return None return TestTimeout(test.timeout.value, test.timeout.message, self._variables, rpa=test.parent.rpa) def _run_setup(self, setup, status, result=None): if not status.failures: exception = self._run_setup_or_teardown(setup) status.setup_executed(exception) if result and isinstance(exception, PassExecution): result.message = exception.message def _run_teardown(self, teardown, status, result=None): if status.teardown_allowed: exception = self._run_setup_or_teardown(teardown) status.teardown_executed(exception) failed = not isinstance(exception, PassExecution) if result and exception: result.message = status.message if failed else exception.message return exception if failed else None def _run_setup_or_teardown(self, data): if not data: return None try: name = self._variables.replace_string(data.name) except DataError as err: if self._settings.dry_run: return None return err if name.upper() in ('', 'NONE'): return None try: StepRunner(self._context).run_step(data, name=name) except ExecutionStatus as err: return err
class SuiteRunner(SuiteVisitor): def __init__(self, output, settings): self.result = None self._output = output self._settings = settings self._variables = VariableScopes(settings) self._suite = None self._suite_status = None self._executed = [NormalizedDict(ignore='_')] self._skipped_tags = TagPatterns(settings.skip) @property def _context(self): return EXECUTION_CONTEXTS.current def start_suite(self, suite): if suite.name in self._executed[-1] and suite.parent.source: self._output.warn( f"Multiple suites with name '{suite.name}' executed in " f"suite '{suite.parent.longname}'.") self._executed[-1][suite.name] = True self._executed.append(NormalizedDict(ignore='_')) self._output.library_listeners.new_suite_scope() result = TestSuite(source=suite.source, name=suite.name, doc=suite.doc, metadata=suite.metadata, starttime=get_timestamp(), rpa=self._settings.rpa) if not self.result: self.result = Result(root_suite=result, rpa=self._settings.rpa) self.result.configure(status_rc=self._settings.status_rc, stat_config=self._settings.statistics_config) else: self._suite.suites.append(result) self._suite = result self._suite_status = SuiteStatus(self._suite_status, self._settings.exit_on_failure, self._settings.exit_on_error, self._settings.skip_teardown_on_exit) ns = Namespace(self._variables, result, suite.resource, self._settings.languages) ns.start_suite() ns.variables.set_from_variable_table(suite.resource.variables) EXECUTION_CONTEXTS.start_suite(result, ns, self._output, self._settings.dry_run) self._context.set_suite_variables(result) if not self._suite_status.failed: ns.handle_imports() ns.variables.resolve_delayed() result.doc = self._resolve_setting(result.doc) result.metadata = [(self._resolve_setting(n), self._resolve_setting(v)) for n, v in result.metadata.items()] self._context.set_suite_variables(result) self._output.start_suite( ModelCombiner(suite, result, tests=suite.tests, suites=suite.suites, test_count=suite.test_count)) self._output.register_error_listener(self._suite_status.error_occurred) self._run_setup(suite.setup, self._suite_status) def _resolve_setting(self, value): if is_list_like(value): return self._variables.replace_list(value, ignore_errors=True) return self._variables.replace_string(value, ignore_errors=True) def end_suite(self, suite): self._suite.message = self._suite_status.message self._context.report_suite_status(self._suite.status, self._suite.full_message) with self._context.suite_teardown(): failure = self._run_teardown(suite.teardown, self._suite_status) if failure: if failure.skip: self._suite.suite_teardown_skipped(str(failure)) else: self._suite.suite_teardown_failed(str(failure)) self._suite.endtime = get_timestamp() self._suite.message = self._suite_status.message self._context.end_suite(ModelCombiner(suite, self._suite)) self._executed.pop() self._suite = self._suite.parent self._suite_status = self._suite_status.parent self._output.library_listeners.discard_suite_scope() def visit_test(self, test): settings = self._settings if test.tags.robot('exclude'): return if test.name in self._executed[-1]: self._output.warn( test_or_task( f"Multiple {{test}}s with name '{test.name}' executed in " f"suite '{test.parent.longname}'.", settings.rpa)) self._executed[-1][test.name] = True result = self._suite.tests.create(self._resolve_setting(test.name), self._resolve_setting(test.doc), self._resolve_setting(test.tags), self._get_timeout(test), test.lineno, starttime=get_timestamp()) self._context.start_test(result) self._output.start_test(ModelCombiner(test, result)) status = TestStatus(self._suite_status, result, settings.skip_on_failure, settings.rpa) if status.exit: self._add_exit_combine() result.tags.add('robot:exit') if status.passed: if not test.name: status.test_failed( test_or_task('{Test} name cannot be empty.', settings.rpa)) elif not test.body: status.test_failed( test_or_task('{Test} contains no keywords.', settings.rpa)) elif test.tags.robot('skip'): status.test_skipped( test_or_task("{Test} skipped using 'robot:skip' tag.", settings.rpa)) elif self._skipped_tags.match(test.tags): status.test_skipped( test_or_task( "{Test} skipped using '--skip' command line option.", settings.rpa)) self._run_setup(test.setup, status, result) if status.passed: try: BodyRunner(self._context, templated=bool(test.template)).run(test.body) except PassExecution as exception: err = exception.earlier_failures if err: status.test_failed(error=err) else: result.message = exception.message except ExecutionStatus as err: status.test_failed(error=err) elif status.skipped: status.test_skipped(status.message) else: status.test_failed(status.message) result.status = status.status result.message = status.message or result.message with self._context.test_teardown(result): self._run_teardown(test.teardown, status, result) if status.passed and result.timeout and result.timeout.timed_out(): status.test_failed(result.timeout.get_message()) result.message = status.message if status.skip_on_failure_after_tag_changes: result.message = status.message or result.message result.status = status.status result.endtime = get_timestamp() failed_before_listeners = result.failed self._output.end_test(ModelCombiner(test, result)) if result.failed and not failed_before_listeners: status.failure_occurred() self._context.end_test(result) def _add_exit_combine(self): exit_combine = ('NOT robot:exit', '') if exit_combine not in self._settings['TagStatCombine']: self._settings['TagStatCombine'].append(exit_combine) def _get_timeout(self, test): if not test.timeout: return None return TestTimeout(test.timeout, self._variables, rpa=test.parent.rpa) def _run_setup(self, setup, status, result=None): if status.passed: exception = self._run_setup_or_teardown(setup) status.setup_executed(exception) if result and isinstance(exception, PassExecution): result.message = exception.message elif status.parent and status.parent.skipped: status.skipped = True def _run_teardown(self, teardown, status, result=None): if status.teardown_allowed: exception = self._run_setup_or_teardown(teardown) status.teardown_executed(exception) failed = exception and not isinstance(exception, PassExecution) if result and exception: if failed or status.skipped or exception.skip: result.message = status.message else: # Pass execution used in teardown, # and it overrides previous failure message result.message = exception.message return exception if failed else None def _run_setup_or_teardown(self, data): if not data: return None try: name = self._variables.replace_string(data.name) except DataError as err: if self._settings.dry_run: return None return ExecutionFailed(message=err.message) if name.upper() in ('', 'NONE'): return None try: KeywordRunner(self._context).run(data, name=name) except ExecutionStatus as err: return err