Robot Framework
suiterunner.py
Go to the documentation of this file.
1 # Copyright 2008-2015 Nokia Networks
2 # Copyright 2016- Robot Framework Foundation
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 
16 from robot.errors import ExecutionFailed, ExecutionStatus, DataError, PassExecution
17 from robot.model import SuiteVisitor, TagPatterns
18 from robot.result import TestSuite, Result
19 from robot.utils import get_timestamp, is_list_like, NormalizedDict, test_or_task
20 from robot.variables import VariableScopes
21 
22 from .bodyrunner import BodyRunner, KeywordRunner
23 from .context import EXECUTION_CONTEXTS
24 from .modelcombiner import ModelCombiner
25 from .namespace import Namespace
26 from .status import SuiteStatus, TestStatus
27 from .timeouts import TestTimeout
28 
29 
31 
32  def __init__(self, output, settings):
33  self.resultresult = None
34  self._output_output = output
35  self._settings_settings = settings
36  self._variables_variables = VariableScopes(settings)
37  self._suite_suite = None
38  self._suite_status_suite_status = None
39  self._executed_executed = [NormalizedDict(ignore='_')]
40  self._skipped_tags_skipped_tags = TagPatterns(settings.skip)
41 
42  @property
43  _context = property
44 
45  def _context(self):
46  return EXECUTION_CONTEXTS.current
47 
48  def start_suite(self, suite):
49  if suite.name in self._executed_executed[-1] and suite.parent.source:
50  self._output_output.warn(f"Multiple suites with name '{suite.name}' executed in "
51  f"suite '{suite.parent.longname}'.")
52  self._executed_executed[-1][suite.name] = True
53  self._executed_executed.append(NormalizedDict(ignore='_'))
54  self._output_output.library_listeners.new_suite_scope()
55  result = TestSuite(source=suite.source,
56  name=suite.name,
57  doc=suite.doc,
58  metadata=suite.metadata,
59  starttime=get_timestamp(),
60  rpa=self._settings_settings.rpa)
61  if not self.resultresult:
62  self.resultresult = Result(root_suite=result, rpa=self._settings_settings.rpa)
63  self.resultresult.configure(status_rc=self._settings_settings.status_rc,
64  stat_config=self._settings_settings.statistics_config)
65  else:
66  self._suite_suite.suites.append(result)
67  self._suite_suite = result
68  self._suite_status_suite_status = SuiteStatus(self._suite_status_suite_status,
69  self._settings_settings.exit_on_failure,
70  self._settings_settings.exit_on_error,
71  self._settings_settings.skip_teardown_on_exit)
72  ns = Namespace(self._variables_variables, result, suite.resource, self._settings_settings.languages)
73  ns.start_suite()
74  ns.variables.set_from_variable_table(suite.resource.variables)
75  EXECUTION_CONTEXTS.start_suite(result, ns, self._output_output,
76  self._settings_settings.dry_run)
77  self._context_context_context.set_suite_variables(result)
78  if not self._suite_status_suite_status.failed:
79  ns.handle_imports()
80  ns.variables.resolve_delayed()
81  result.doc = self._resolve_setting_resolve_setting(result.doc)
82  result.metadata = [(self._resolve_setting_resolve_setting(n), self._resolve_setting_resolve_setting(v))
83  for n, v in result.metadata.items()]
84  self._context_context_context.set_suite_variables(result)
85  self._output_output.start_suite(ModelCombiner(suite, result,
86  tests=suite.tests,
87  suites=suite.suites,
88  test_count=suite.test_count))
89  self._output_output.register_error_listener(self._suite_status_suite_status.error_occurred)
90  self._run_setup_run_setup(suite.setup, self._suite_status_suite_status)
91 
92  def _resolve_setting(self, value):
93  if is_list_like(value):
94  return self._variables_variables.replace_list(value, ignore_errors=True)
95  return self._variables_variables.replace_string(value, ignore_errors=True)
96 
97  def end_suite(self, suite):
98  self._suite_suite.message = self._suite_status_suite_status.message
99  self._context_context_context.report_suite_status(self._suite_suite.status,
100  self._suite_suite.full_message)
101  with self._context_context_context.suite_teardown():
102  failure = self._run_teardown_run_teardown(suite.teardown, self._suite_status_suite_status)
103  if failure:
104  if failure.skip:
105  self._suite_suite.suite_teardown_skipped(str(failure))
106  else:
107  self._suite_suite.suite_teardown_failed(str(failure))
108  self._suite_suite.endtime = get_timestamp()
109  self._suite_suite.message = self._suite_status_suite_status.message
110  self._context_context_context.end_suite(ModelCombiner(suite, self._suite_suite))
111  self._executed_executed.pop()
112  self._suite_suite = self._suite_suite.parent
113  self._suite_status_suite_status = self._suite_status_suite_status.parent
114  self._output_output.library_listeners.discard_suite_scope()
115 
116  def visit_test(self, test):
117  settings = self._settings_settings
118  if test.tags.robot('exclude'):
119  return
120  if test.name in self._executed_executed[-1]:
121  self._output_output.warn(
122  test_or_task(f"Multiple {{test}}s with name '{test.name}' executed in "
123  f"suite '{test.parent.longname}'.", settings.rpa))
124  self._executed_executed[-1][test.name] = True
125  result = self._suite_suite.tests.create(self._resolve_setting_resolve_setting(test.name),
126  self._resolve_setting_resolve_setting(test.doc),
127  self._resolve_setting_resolve_setting(test.tags),
128  self._get_timeout_get_timeout(test),
129  test.lineno,
130  starttime=get_timestamp())
131  self._context_context_context.start_test(result)
132  self._output_output.start_test(ModelCombiner(test, result))
133  status = TestStatus(self._suite_status_suite_status, result, settings.skip_on_failure,
134  settings.rpa)
135  if status.exit:
136  self._add_exit_combine_add_exit_combine()
137  result.tags.add('robot:exit')
138  if status.passed:
139  if not test.name:
140  status.test_failed(
141  test_or_task('{Test} name cannot be empty.', settings.rpa))
142  elif not test.body:
143  status.test_failed(
144  test_or_task('{Test} contains no keywords.', settings.rpa))
145  elif test.tags.robot('skip'):
146  status.test_skipped(
147  test_or_task("{Test} skipped using 'robot:skip' tag.",
148  settings.rpa))
149  elif self._skipped_tags_skipped_tags.match(test.tags):
150  status.test_skipped(
151  test_or_task("{Test} skipped using '--skip' command line option.",
152  settings.rpa))
153  self._run_setup_run_setup(test.setup, status, result)
154  if status.passed:
155  try:
156  BodyRunner(self._context_context_context, templated=bool(test.template)).run(test.body)
157  except PassExecution as exception:
158  err = exception.earlier_failures
159  if err:
160  status.test_failed(error=err)
161  else:
162  result.message = exception.message
163  except ExecutionStatus as err:
164  status.test_failed(error=err)
165  elif status.skipped:
166  status.test_skipped(status.message)
167  else:
168  status.test_failed(status.message)
169  result.status = status.status
170  result.message = status.message or result.message
171  with self._context_context_context.test_teardown(result):
172  self._run_teardown_run_teardown(test.teardown, status, result)
173  if status.passed and result.timeout and result.timeout.timed_out():
174  status.test_failed(result.timeout.get_message())
175  result.message = status.message
176  if status.skip_on_failure_after_tag_changes:
177  result.message = status.message or result.message
178  result.status = status.status
179  result.endtime = get_timestamp()
180  failed_before_listeners = result.failed
181  self._output_output.end_test(ModelCombiner(test, result))
182  if result.failed and not failed_before_listeners:
183  status.failure_occurred()
184  self._context_context_context.end_test(result)
185 
186  def _add_exit_combine(self):
187  exit_combine = ('NOT robot:exit', '')
188  if exit_combine not in self._settings_settings['TagStatCombine']:
189  self._settings_settings['TagStatCombine'].append(exit_combine)
190 
191  def _get_timeout(self, test):
192  if not test.timeout:
193  return None
194  return TestTimeout(test.timeout, self._variables_variables, rpa=test.parent.rpa)
195 
196  def _run_setup(self, setup, status, result=None):
197  if status.passed:
198  exception = self._run_setup_or_teardown_run_setup_or_teardown(setup)
199  status.setup_executed(exception)
200  if result and isinstance(exception, PassExecution):
201  result.message = exception.message
202  elif status.parent and status.parent.skipped:
203  status.skipped = True
204 
205  def _run_teardown(self, teardown, status, result=None):
206  if status.teardown_allowed:
207  exception = self._run_setup_or_teardown_run_setup_or_teardown(teardown)
208  status.teardown_executed(exception)
209  failed = exception and not isinstance(exception, PassExecution)
210  if result and exception:
211  if failed or status.skipped or exception.skip:
212  result.message = status.message
213  else:
214  # Pass execution used in teardown,
215  # and it overrides previous failure message
216  result.message = exception.message
217  return exception if failed else None
218 
219  def _run_setup_or_teardown(self, data):
220  if not data:
221  return None
222  try:
223  name = self._variables_variables.replace_string(data.name)
224  except DataError as err:
225  if self._settings_settings.dry_run:
226  return None
227  return ExecutionFailed(message=err.message)
228  if name.upper() in ('', 'NONE'):
229  return None
230  try:
231  KeywordRunner(self._context_context_context).run(data, name=name)
232  except ExecutionStatus as err:
233  return err
Used for communicating failures in test execution.
Definition: errors.py:178
Interface to ease traversing through a test suite structure.
Definition: visitor.py:85
def end_test(self, test)
Called when a test ends.
Definition: visitor.py:136
def start_test(self, test)
Called when a test starts.
Definition: visitor.py:132
Represents results of a single test suite.
Definition: model.py:602
def end_suite(self, suite)
Called when a suite ends.
Definition: suiterunner.py:97
def __init__(self, output, settings)
Definition: suiterunner.py:32
def _run_setup(self, setup, status, result=None)
Definition: suiterunner.py:196
def visit_test(self, test)
Implements traversing through tests.
Definition: suiterunner.py:116
def start_suite(self, suite)
Called when a suite starts.
Definition: suiterunner.py:48
def _run_teardown(self, teardown, status, result=None)
Definition: suiterunner.py:205
def warn(msg, html=False)
Writes the message to the log file using the WARN level.
Definition: logger.py:120
def run(*tests, **options)
Programmatic entry point for running tests.
Definition: run.py:557
def test_or_task(str text, bool rpa)
Replace 'test' with 'task' in the given text depending on rpa.
Definition: misc.py:105
def get_timestamp(daysep='', daytimesep=' ', timesep=':', millissep='.')
Definition: robottime.py:335
def is_list_like(item)
Definition: robottypes.py:66