forked from robotframework/robotframework
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstatus.py
More file actions
315 lines (258 loc) · 10.9 KB
/
Copy pathstatus.py
File metadata and controls
315 lines (258 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# 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 ExecutionStatus, PassExecution
from robot.model import TagPatterns
from robot.utils import html_escape, unic, test_or_task
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
)
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
def failure_occurred(self, failure=None):
if isinstance(failure, ExecutionStatus) and failure.exit:
self.fatal = True
if self.failure_mode:
self.failure = True
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 self.failure or self.error or self.fatal
class _ExecutionStatus:
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.skipped = False
self._teardown_allowed = False
self._rpa = False
if parent:
parent.children.append(self)
def setup_executed(self, failure=None):
if failure and not isinstance(failure, PassExecution):
if failure.skip:
self.failure.setup_skipped = unic(failure)
self.skipped = True
elif self._skip_on_failure():
msg = self._skip_on_failure_message('Setup failed:\n%s' % failure)
self.failure.test = msg
self.skipped = True
else:
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):
if failure.skip:
self.failure.teardown_skipped = unic(failure)
# Keep the Skip status in case the teardown failed
self.skipped = self.skipped or failure.skip
elif self._skip_on_failure():
msg = self._skip_on_failure_message('Teardown failed:\n%s' % failure)
self.failure.test = msg
self.skipped = True
else:
self.failure.teardown = unic(failure)
self.exit.failure_occurred(failure)
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 failed(self):
return bool(self.parent and self.parent.failed or self.failure or self.exit)
@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_failure_message(self, failure):
return test_or_task(
"{Test} failed but its tags matched '--SkipOnFailure' and it was marked "
"skipped.\n\nOriginal failure:\n%s" % unic(failure), rpa=self._rpa
)
@property
def message(self):
if self.failure or self.exit:
return self._my_message()
if self.parent and self.parent.failed:
return self._parent_message()
return ''
def _my_message(self):
raise NotImplementedError
def _parent_message(self):
return ParentMessage(self.parent).message
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
class TestStatus(_ExecutionStatus):
def __init__(self, parent, test, skip_on_failure=None, critical_tags=None,
rpa=False):
_ExecutionStatus.__init__(self, parent)
self.exit = parent.exit
self._test = test
self._skip_on_failure_tags = skip_on_failure
self._critical_tags = critical_tags
self._rpa = rpa
def test_failed(self, failure):
if hasattr(failure, 'skip') and failure.skip:
self.test_skipped(failure)
elif self._skip_on_failure():
msg = self._skip_on_failure_message(failure)
self.failure.test = msg
self.skipped = True
else:
self.failure.test = unic(failure)
self.exit.failure_occurred(failure)
def test_skipped(self, reason):
self.skipped = True
self.failure.test_skipped = unic(reason)
def skip_if_needed(self):
if not self.skipped and self.failed and self._skip_on_failure():
msg = self._skip_on_failure_message(self.failure.test)
self.failure.test = msg
self.skipped = True
return True
return False
def _skip_on_failure(self):
tags = self._test.tags
critical_pattern = TagPatterns(self._critical_tags)
critical = not critical_pattern or critical_pattern.match(tags)
skip_on_fail_pattern = TagPatterns(self._skip_on_failure_tags)
skip_on_fail = skip_on_fail_pattern and skip_on_fail_pattern.match(tags)
return not critical or skip_on_fail
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)
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(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 ''
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'
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)