Skip to content

Commit 2b4920a

Browse files
committed
Handle skip using special exception attribute, not type.
This is consistent with how fatal and continuable errors are handled. Also rename `_ExecutionStatus.failures` (a Boolean) to `failed` to be consistent with `skipped` and easier to separate from `failure` (a string).
1 parent 0811e3a commit 2b4920a

File tree

4 files changed

+32
-26
lines changed

4 files changed

+32
-26
lines changed

src/robot/errors.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class ExecutionStatus(RobotError):
108108

109109
def __init__(self, message, test_timeout=False, keyword_timeout=False,
110110
syntax=False, exit=False, continue_on_failure=False,
111-
return_value=None):
111+
skip=False, return_value=None):
112112
if '\r\n' in message:
113113
message = message.replace('\r\n', '\n')
114114
from robot.utils import cut_long_message
@@ -118,6 +118,7 @@ def __init__(self, message, test_timeout=False, keyword_timeout=False,
118118
self.syntax = syntax
119119
self.exit = exit
120120
self._continue_on_failure = continue_on_failure
121+
self.skip = skip
121122
self.return_value = return_value
122123

123124
@property
@@ -157,7 +158,7 @@ def get_errors(self):
157158

158159
@property
159160
def status(self):
160-
return 'FAIL'
161+
return 'FAIL' if not self.skip else 'SKIP'
161162

162163

163164
class ExecutionFailed(ExecutionStatus):
@@ -175,9 +176,10 @@ def __init__(self, details):
175176
and not isinstance(error, (KeywordError, VariableError)))
176177
exit_on_failure = self._get(error, 'EXIT_ON_FAILURE')
177178
continue_on_failure = self._get(error, 'CONTINUE_ON_FAILURE')
179+
skip = self._get(error, 'SKIP_ON_FAILURE')
178180
ExecutionFailed.__init__(self, details.message, test_timeout,
179181
keyword_timeout, syntax, exit_on_failure,
180-
continue_on_failure)
182+
continue_on_failure, skip)
181183
self.full_message = details.message
182184
self.traceback = details.traceback
183185

@@ -218,7 +220,8 @@ def _get_attrs(self, errors):
218220
'keyword_timeout': any(e.keyword_timeout for e in errors),
219221
'syntax': any(e.syntax for e in errors),
220222
'exit': any(e.exit for e in errors),
221-
'continue_on_failure': all(e.continue_on_failure for e in errors)
223+
'continue_on_failure': all(e.continue_on_failure for e in errors),
224+
'skip': all(e.skip for e in errors)
222225
}
223226

224227
def get_errors(self):
@@ -286,16 +289,15 @@ def __init__(self, message):
286289
ExecutionPassed.__init__(self, message)
287290

288291

289-
class SkipExecution(ExecutionStatus):
292+
# FIXME: Move under robot.api.
293+
class SkipExecution(Exception):
290294
"""Used by 'Skip' keyword.
291295
292296
Can also be used by library keyword to skip the current test.
293297
"""
294-
status = "SKIP"
298+
# TODO: Perhaps misleading attr name as this isn't related to --skiponfailure?
295299
ROBOT_SKIP_ON_FAILURE = True
296-
297-
def __init__(self, message):
298-
ExecutionStatus.__init__(self, message)
300+
ROBOT_SUPPRESS_NAME = True
299301

300302

301303
class ContinueForLoop(ExecutionPassed):

src/robot/running/runner.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16-
from robot.errors import ExecutionStatus, DataError, PassExecution, SkipExecution
16+
from robot.errors import ExecutionStatus, DataError, PassExecution
1717
from robot.model import SuiteVisitor, TagPatterns
1818
from robot.result import TestSuite, Result
1919
from robot.utils import get_timestamp, is_list_like, NormalizedDict, unic
@@ -69,7 +69,7 @@ def start_suite(self, suite):
6969
EXECUTION_CONTEXTS.start_suite(result, ns, self._output,
7070
self._settings.dry_run)
7171
self._context.set_suite_variables(result)
72-
if not self._suite_status.failures:
72+
if not self._suite_status.failed:
7373
ns.handle_imports()
7474
ns.variables.resolve_delayed()
7575
result.doc = self._resolve_setting(result.doc)
@@ -126,13 +126,13 @@ def visit_test(self, test):
126126
# TODO: helper for resolving test/tags
127127
if self._skipped_tags.match(test.tags):
128128
status.test_skipped("Test skipped with --skip command line option.")
129-
if not status.failures and not test.name:
129+
if not status.failed and not test.name:
130130
status.test_failed('Test case name cannot be empty.')
131-
if not status.failures and not test.keywords.normal:
131+
if not status.failed and not test.keywords.normal:
132132
status.test_failed('Test case contains no keywords.')
133133
self._run_setup(test.keywords.setup, status, result)
134134
try:
135-
if not status.failures:
135+
if not status.failed:
136136
StepRunner(self._context,
137137
test.template).run_steps(test.keywords.normal)
138138
else:
@@ -159,7 +159,7 @@ def visit_test(self, test):
159159
result)
160160
if failure:
161161
status.failure_occurred()
162-
if not status.failures and result.timeout and result.timeout.timed_out():
162+
if not status.failed and result.timeout and result.timeout.timed_out():
163163
status.test_failed(result.timeout.get_message())
164164
result.message = status.message
165165
result.status = status.status
@@ -178,7 +178,7 @@ def _get_timeout(self, test):
178178
return TestTimeout(test.timeout, self._variables, rpa=test.parent.rpa)
179179

180180
def _run_setup(self, setup, status, result=None):
181-
if not status.failures:
181+
if not status.failed:
182182
exception = self._run_setup_or_teardown(setup)
183183
status.setup_executed(exception)
184184
if result and isinstance(exception, PassExecution):
@@ -191,7 +191,9 @@ def _run_teardown(self, teardown, status, result=None):
191191
if status.teardown_allowed:
192192
exception = self._run_setup_or_teardown(teardown)
193193
status.teardown_executed(exception)
194-
failed = not isinstance(exception, PassExecution) and not isinstance(exception, SkipExecution)
194+
failed = (exception
195+
and not isinstance(exception, PassExecution)
196+
and not exception.skip)
195197
if result and exception:
196198
result.message = status.message if failed else exception.message
197199
return exception if failed else None

src/robot/running/status.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16-
from robot.errors import ExecutionFailed, PassExecution, SkipExecution
16+
from robot.errors import ExecutionFailed, PassExecution
1717
from robot.model.tags import TagPatterns
1818
from robot.utils import html_escape, py2to3, unic
1919

@@ -76,13 +76,14 @@ def setup_executed(self, failure=None):
7676
if failure and not isinstance(failure, PassExecution):
7777
self.failure.setup = unic(failure)
7878
self.exit.failure_occurred(failure)
79-
self.skipped = isinstance(failure, SkipExecution)
79+
self.skipped = failure.skip
8080
self._teardown_allowed = True
8181

8282
def teardown_executed(self, failure=None):
8383
if failure and not isinstance(failure, PassExecution):
8484
self.failure.teardown = unic(failure)
8585
self.exit.failure_occurred(failure)
86+
# TODO: Handle skip in teardown!
8687

8788
def failure_occurred(self):
8889
self.exit.failure_occurred()
@@ -95,21 +96,23 @@ def teardown_allowed(self):
9596
return self.exit.teardown_allowed and self._teardown_allowed
9697

9798
@property
98-
def failures(self):
99-
return bool(self.parent and self.parent.failures or
99+
def failed(self):
100+
return bool(self.parent and self.parent.failed or
100101
self.failure or self.exit)
101102

102103
@property
103104
def status(self):
104105
if self.skipped:
105106
return 'SKIP'
106-
return 'FAIL' if self.failures else 'PASS'
107+
if self.failed:
108+
return 'FAIL'
109+
return 'PASS'
107110

108111
@property
109112
def message(self):
110113
if self.failure or self.exit:
111114
return self._my_message()
112-
if self.parent and self.parent.failures:
115+
if self.parent and self.parent.failed:
113116
return self._parent_message()
114117
return ''
115118

@@ -258,6 +261,6 @@ class ParentMessage(SuiteMessage):
258261
also_teardown_message = '%s\n\nAlso parent suite teardown failed:\n%s'
259262

260263
def __init__(self, status):
261-
while status.parent and status.parent.failures:
264+
while status.parent and status.parent.failed:
262265
status = status.parent
263266
SuiteMessage.__init__(self, status)

src/robot/running/statusreporter.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414
# limitations under the License.
1515

1616
from robot.errors import (ExecutionFailed, ExecutionStatus, DataError,
17-
HandlerExecutionFailed, KeywordError, VariableError,
18-
SkipExecution)
17+
HandlerExecutionFailed, KeywordError, VariableError)
1918
from robot.utils import ErrorDetails, get_timestamp
2019

2120

0 commit comments

Comments
 (0)