Source code for robot.running.status

#  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 PassExecution
from robot.model import TagPatterns
from robot.utils import html_escape, test_or_task


[docs] class Failure: def __init__(self): self.setup = None self.test = None self.teardown = None self.setup_skipped = None self.test_skipped = None self.teardown_skipped = None def __bool__(self): return bool( self.setup or self.test or self.teardown or self.setup_skipped or self.test_skipped or self.teardown_skipped )
[docs] class Exit: 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, fatal=False): if fatal: self.fatal = True if 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 __bool__(self): return bool(self.failure or self.error or self.fatal)
class _ExecutionStatus: def __init__(self, parent, exit=None): self.parent = parent self.exit = exit if exit is not None else parent.exit self.failure = Failure() self.skipped = False self._teardown_allowed = False self._rpa = False @property def failed(self): return bool(self.parent and self.parent.failed or self.failure or self.exit) @property def passed(self): return not self.failed def setup_executed(self, error=None): if error and not isinstance(error, PassExecution): msg = str(error) if error.skip: self.failure.setup_skipped = msg self.skipped = True elif self._skip_on_failure(): self.failure.test = self._skip_on_fail_msg(f'Setup failed:\n{msg}') self.skipped = True else: self.failure.setup = msg self.exit.failure_occurred(error.exit) self._teardown_allowed = True def teardown_executed(self, error=None): if error and not isinstance(error, PassExecution): msg = str(error) if error.skip: self.failure.teardown_skipped = msg self.skipped = True elif self._skip_on_failure(): self.failure.test = self._skip_on_fail_msg(f'Teardown failed:\n{msg}') self.skipped = True else: self.failure.teardown = msg self.exit.failure_occurred(error.exit) def failure_occurred(self): self.exit.failure_occurred() def error_occurred(self): self.exit.error_occurred() @property def teardown_allowed(self): return self.exit.teardown_allowed and self._teardown_allowed @property def status(self): if self.skipped or (self.parent and self.parent.skipped): return 'SKIP' if self.failed: return 'FAIL' return 'PASS' def _skip_on_failure(self): return False def _skip_on_fail_msg(self, msg): return msg @property def message(self): if self.failure or self.exit: return self._my_message() if self.parent and not self.parent.passed: 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=False, exit_on_error=False, skip_teardown_on_exit=False): if parent is None: exit = Exit(exit_on_failure, exit_on_error, skip_teardown_on_exit) else: exit = None super().__init__(parent, exit) def _my_message(self): return SuiteMessage(self).message
[docs] class TestStatus(_ExecutionStatus): def __init__(self, parent, test, skip_on_failure=None, rpa=False): super().__init__(parent) self._test = test self._skip_on_failure_tags = skip_on_failure self._rpa = rpa
[docs] def test_failed(self, message=None, error=None): if error is not None: message = str(error) skip = error.skip fatal = error.exit else: skip = fatal = False if skip: self.test_skipped(message) elif self._skip_on_failure(): self.failure.test = self._skip_on_fail_msg(message) self.skipped = True else: self.failure.test = message self.exit.failure_occurred(fatal)
[docs] def test_skipped(self, message): self.skipped = True self.failure.test_skipped = message
@property def skip_on_failure_after_tag_changes(self): if not self.skipped and self.failed and self._skip_on_failure(): self.failure.test = self._skip_on_fail_msg(self.failure.test) self.skipped = True return True return False def _skip_on_failure(self): return (self._test.tags.robot('skip-on-failure') or self._skip_on_failure_tags and TagPatterns(self._skip_on_failure_tags).match(self._test.tags)) def _skip_on_fail_msg(self, msg): return test_or_task( "{Test} failed but skip-on-failure mode was active and it was marked " "skipped.\n\nOriginal failure:\n%s" % msg, rpa=self._rpa ) def _my_message(self): return TestMessage(self).message
class _Message: setup_message = NotImplemented setup_skipped_message = NotImplemented teardown_skipped_message = NotImplemented teardown_message = NotImplemented also_teardown_message = NotImplemented def __init__(self, status): self.failure = status.failure self.skipped = status.skipped @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_skipped: return self._format_setup_or_teardown_message( self.setup_skipped_message, self.failure.setup_skipped) if self.failure.setup: return self._format_setup_or_teardown_message( self.setup_message, self.failure.setup) return self.failure.test_skipped or 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 or self.failure.teardown_skipped): return message if not message: if self.failure.teardown: prefix, msg = self.teardown_message, self.failure.teardown else: prefix, msg = self.teardown_skipped_message, self.failure.teardown_skipped return self._format_setup_or_teardown_message(prefix, msg) return self._format_message_with_teardown_message(message) def _format_message_with_teardown_message(self, message): teardown = self.failure.teardown or self.failure.teardown_skipped 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) if self.failure.teardown: return self.also_teardown_message % (message, teardown) return self.also_teardown_skip_message % (teardown, message)
[docs] class TestMessage(_Message): setup_message = 'Setup failed:\n%s' teardown_message = 'Teardown failed:\n%s' setup_skipped_message = '%s' teardown_skipped_message = '%s' also_teardown_message = '%s\n\nAlso teardown failed:\n%s' also_teardown_skip_message = 'Skipped in teardown:\n%s\n\nEarlier message:\n%s' exit_on_fatal_message = 'Test execution stopped due to a fatal error.' exit_on_failure_message = \ '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().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' setup_skipped_message = 'Skipped in suite setup:\n%s' teardown_skipped_message = 'Skipped in suite teardown:\n%s' teardown_message = 'Suite teardown failed:\n%s' also_teardown_message = '%s\n\nAlso suite teardown failed:\n%s' also_teardown_skip_message = 'Skipped in suite teardown:\n%s\n\nEarlier message:\n%s'
[docs] class ParentMessage(SuiteMessage): setup_message = 'Parent suite setup failed:\n%s' setup_skipped_message = 'Skipped in parent suite setup:\n%s' teardown_skipped_message = 'Skipped in parent suite teardown:\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.failed: status = status.parent SuiteMessage.__init__(self, status)