# Copyright 2008-2015 Nokia Networks
# Copyright 2016- Robot Framework Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from robot.errors import ExecutionFailed, PassExecution
from robot.utils import html_escape, py2to3, unic
[docs]@py2to3
class Failure(object):
def __init__(self):
self.setup = None
self.test = None
self.teardown = None
def __nonzero__(self):
return bool(self.setup or self.test or self.teardown)
[docs]@py2to3
class Exit(object):
def __init__(self, failure_mode=False, error_mode=False,
skip_teardown_mode=False):
self.failure_mode = failure_mode
self.error_mode = error_mode
self.skip_teardown_mode = skip_teardown_mode
self.failure = False
self.error = False
self.fatal = False
[docs] def failure_occurred(self, failure=None, critical=False):
if isinstance(failure, ExecutionFailed) and failure.exit:
self.fatal = True
if critical and self.failure_mode:
self.failure = True
[docs] def error_occurred(self):
if self.error_mode:
self.error = True
@property
def teardown_allowed(self):
return not (self.skip_teardown_mode and self)
def __nonzero__(self):
return self.failure or self.error or self.fatal
class _ExecutionStatus(object):
def __init__(self, parent=None, *exit_modes):
self.parent = parent
self.children = []
self.failure = Failure()
self.exit = parent.exit if parent else Exit(*exit_modes)
self._teardown_allowed = False
if parent:
parent.children.append(self)
def setup_executed(self, failure=None):
if failure and not isinstance(failure, PassExecution):
self.failure.setup = unic(failure)
self.exit.failure_occurred(failure)
self._teardown_allowed = True
def teardown_executed(self, failure=None):
if failure and not isinstance(failure, PassExecution):
self.failure.teardown = unic(failure)
self.exit.failure_occurred(failure)
def critical_failure_occurred(self):
self.exit.failure_occurred(critical=True)
def error_occurred(self):
self.exit.error_occurred()
@property
def teardown_allowed(self):
return self.exit.teardown_allowed and self._teardown_allowed
@property
def failures(self):
return bool(self.parent and self.parent.failures or
self.failure or self.exit)
def _parent_failures(self):
return self.parent and self.parent.failures
@property
def status(self):
return 'FAIL' if self.failures else 'PASS'
@property
def message(self):
if self.failure or self.exit:
return self._my_message()
if self.parent and self.parent.failures:
return self._parent_message()
return ''
def _my_message(self):
raise NotImplementedError
def _parent_message(self):
return ParentMessage(self.parent).message
[docs]class SuiteStatus(_ExecutionStatus):
def __init__(self, parent=None, exit_on_failure_mode=False,
exit_on_error_mode=False,
skip_teardown_on_exit_mode=False):
_ExecutionStatus.__init__(self, parent, exit_on_failure_mode,
exit_on_error_mode,
skip_teardown_on_exit_mode)
def _my_message(self):
return SuiteMessage(self).message
[docs]class TestStatus(_ExecutionStatus):
def __init__(self, parent, critical):
_ExecutionStatus.__init__(self, parent)
self.exit = parent.exit
self._critical = critical
[docs] def test_failed(self, failure):
self.failure.test = unic(failure)
self.exit.failure_occurred(failure, self._critical)
def _my_message(self):
return TestMessage(self).message
class _Message(object):
setup_message = NotImplemented
teardown_message = NotImplemented
also_teardown_message = NotImplemented
def __init__(self, status):
self.failure = status.failure
@property
def message(self):
message = self._get_message_before_teardown()
return self._get_message_after_teardown(message)
def _get_message_before_teardown(self):
if self.failure.setup:
return self._format_setup_or_teardown_message(self.setup_message,
self.failure.setup)
return self.failure.test or ''
def _format_setup_or_teardown_message(self, prefix, message):
if message.startswith('*HTML*'):
prefix = '*HTML* ' + prefix
message = message[6:].lstrip()
return prefix % message
def _get_message_after_teardown(self, message):
if not self.failure.teardown:
return message
if not message:
return self._format_setup_or_teardown_message(self.teardown_message,
self.failure.teardown)
return self._format_message_with_teardown_message(message)
def _format_message_with_teardown_message(self, message):
teardown = self.failure.teardown
if teardown.startswith('*HTML*'):
teardown = teardown[6:].lstrip()
if not message.startswith('*HTML*'):
message = '*HTML* ' + html_escape(message)
elif message.startswith('*HTML*'):
teardown = html_escape(teardown)
return self.also_teardown_message % (message, teardown)
[docs]class TestMessage(_Message):
setup_message = 'Setup failed:\n%s'
teardown_message = 'Teardown failed:\n%s'
also_teardown_message = '%s\n\nAlso teardown failed:\n%s'
exit_on_fatal_message = 'Test execution stopped due to a fatal error.'
exit_on_failure_message = \
'Critical failure occurred and exit-on-failure mode is in use.'
exit_on_error_message = 'Error occurred and exit-on-error mode is in use.'
def __init__(self, status):
_Message.__init__(self, status)
self.exit = status.exit
@property
def message(self):
message = super(TestMessage, self).message
if message:
return message
if self.exit.failure:
return self.exit_on_failure_message
if self.exit.fatal:
return self.exit_on_fatal_message
if self.exit.error:
return self.exit_on_error_message
return ''
[docs]class SuiteMessage(_Message):
setup_message = 'Suite setup failed:\n%s'
teardown_message = 'Suite teardown failed:\n%s'
also_teardown_message = '%s\n\nAlso suite teardown failed:\n%s'
[docs]class ParentMessage(SuiteMessage):
setup_message = 'Parent suite setup failed:\n%s'
teardown_message = 'Parent suite teardown failed:\n%s'
also_teardown_message = '%s\n\nAlso parent suite teardown failed:\n%s'
def __init__(self, status):
while status.parent and status.parent.failures:
status = status.parent
SuiteMessage.__init__(self, status)