Skip to content

Commit 9d478ca

Browse files
committed
Add more exception logic and tests
1 parent 158e2c2 commit 9d478ca

File tree

5 files changed

+111
-73
lines changed

5 files changed

+111
-73
lines changed

robotframework_reportportal/listener.py

Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from reportportal_client.helpers import LifoQueue, guess_content_type_from_bytes, is_binary
2828

2929
from robotframework_reportportal.helpers import _unescape, match_pattern, translate_glob_to_regex
30-
from robotframework_reportportal.model import Keyword, Launch, LogMessage, Suite, Test
30+
from robotframework_reportportal.model import Keyword, Launch, LogMessage, Suite, Test, Entity
3131
from robotframework_reportportal.service import RobotService
3232
from robotframework_reportportal.static import MAIN_SUITE_ID, PABOT_WITHOUT_LAUNCH_ID_MSG
3333
from robotframework_reportportal.variables import Variables
@@ -150,8 +150,7 @@ def _build_msg_struct(self, message: Dict[str, Any]) -> LogMessage:
150150
else:
151151
msg = LogMessage(message["message"])
152152
msg.level = message["level"]
153-
if not msg.launch_log:
154-
msg.item_id = getattr(self.current_item, "rp_item_id", None)
153+
msg.item_id = self.current_item.rp_item_id
155154

156155
message_str = msg.message
157156
if is_binary(message_str):
@@ -218,42 +217,52 @@ def __post_log_message(self, message: LogMessage) -> None:
218217
logger.debug(f"ReportPortal - Log Message: {message}")
219218
self.service.log(message=message)
220219

221-
def __post_skipped_keyword(self, kwd: Keyword) -> None:
220+
def __post_skipped_keyword(self, kwd: Keyword, clean_data_remove: bool) -> None:
222221
self._do_start_keyword(kwd)
222+
if clean_data_remove:
223+
kwd.remove_data = False
223224
for log_message in kwd.skipped_logs:
224225
self.__post_log_message(log_message)
225-
skipped_kwds = kwd.skipped_keywords
226+
skipped_keywords = kwd.skipped_keywords
226227
kwd.skipped_keywords = []
227-
for skipped_kwd in skipped_kwds:
228-
self.__post_skipped_keyword(skipped_kwd)
229-
self._do_end_keyword(kwd)
228+
for skipped_kwd in skipped_keywords:
229+
self.__post_skipped_keyword(skipped_kwd, clean_data_remove)
230+
if kwd.status != "NOT SET":
231+
self._do_end_keyword(kwd)
230232

231-
def _post_skipped_keywords(self, to_post: Optional[Any]) -> None:
233+
def _post_skipped_keywords(self, to_post: Optional[Any], clean_data_remove: bool = False) -> None:
232234
if not to_post:
233235
return
234236
if isinstance(to_post, Keyword):
235237
if not to_post.posted:
236238
self._do_start_keyword(to_post)
239+
if clean_data_remove:
240+
to_post.remove_data = False
237241
log_messages = to_post.skipped_logs
238242
to_post.skipped_logs = []
239243
for log_message in log_messages:
240244
self.__post_log_message(log_message)
241-
skipped_kwds = to_post.skipped_keywords
242-
if skipped_kwds:
245+
skipped_keywords = to_post.skipped_keywords
246+
if skipped_keywords:
243247
to_post.skipped_keywords = []
244-
for skipped_kwd in skipped_kwds:
248+
for skipped_kwd in skipped_keywords:
245249
if skipped_kwd.posted:
246250
log_messages = skipped_kwd.skipped_logs
247251
skipped_kwd.skipped_logs = []
248252
for log_message in log_messages:
249253
self.__post_log_message(log_message)
250-
skipped_child_kwds = skipped_kwd.skipped_keywords
251-
for skipped_child_kwd in skipped_child_kwds:
254+
skipped_child_keywords = skipped_kwd.skipped_keywords
255+
for skipped_child_kwd in skipped_child_keywords:
252256
if skipped_child_kwd.posted:
253257
continue
254-
self.__post_skipped_keyword(skipped_child_kwd)
258+
self.__post_skipped_keyword(skipped_child_kwd, clean_data_remove)
255259
continue
256-
self.__post_skipped_keyword(skipped_kwd)
260+
self.__post_skipped_keyword(skipped_kwd, clean_data_remove)
261+
262+
def __find_root_keyword_with_removed_data(self, keyword: Entity) -> Entity:
263+
if keyword.parent.remove_data and keyword.parent.type == "KEYWORD":
264+
return self.__find_root_keyword_with_removed_data(keyword.parent)
265+
return keyword
257266

258267
def _log_message(self, message: LogMessage) -> None:
259268
"""Send log message to the Report Portal.
@@ -274,10 +283,8 @@ def _log_message(self, message: LogMessage) -> None:
274283
else:
275284
if not self._remove_all_keyword_content:
276285
# Post everything skipped by '--removekeywords' option
277-
self._post_skipped_keywords(current_item)
286+
self._post_skipped_keywords(self.__find_root_keyword_with_removed_data(current_item), True)
278287
self.__post_log_message(message)
279-
else:
280-
self.current_item.skipped_logs.append(message)
281288

282289
@check_rp_enabled
283290
def log_message(self, message: Dict) -> None:
@@ -304,11 +311,6 @@ def log_message_with_image(self, msg: Dict, image: str):
304311
}
305312
self._log_message(mes)
306313

307-
@property
308-
def parent_id(self) -> Optional[str]:
309-
"""Get rp_item_id attribute of the current item."""
310-
return getattr(self.current_item, "rp_item_id", None)
311-
312314
@property
313315
def service(self) -> RobotService:
314316
"""Initialize instance of the RobotService."""
@@ -409,8 +411,7 @@ def start_suite(self, name: str, attributes: Dict, ts: Optional[Any] = None) ->
409411
logger.debug(f"ReportPortal - Create global Suite: {attributes}")
410412
else:
411413
logger.debug(f"ReportPortal - Start Suite: {attributes}")
412-
suite = Suite(name, attributes)
413-
suite.rp_parent_item_id = self.parent_id
414+
suite = Suite(name, attributes, self.current_item)
414415
suite.rp_item_id = self.service.start_suite(suite=suite, ts=ts)
415416
self._add_current_item(suite)
416417

@@ -440,9 +441,8 @@ def start_test(self, name: str, attributes: Dict, ts: Optional[Any] = None) -> N
440441
# no 'source' parameter at this level for Robot versions < 4
441442
attributes = attributes.copy()
442443
attributes["source"] = getattr(self.current_item, "source", None)
443-
test = Test(name=name, robot_attributes=attributes, test_attributes=self.variables.test_attributes)
444+
test = Test(name, attributes, self.variables.test_attributes, self.current_item)
444445
logger.debug(f"ReportPortal - Start Test: {attributes}")
445-
test.rp_parent_item_id = self.parent_id
446446
test.rp_item_id = self.service.start_test(test=test, ts=ts)
447447
self._add_current_item(test)
448448

@@ -489,9 +489,8 @@ def start_keyword(self, name: str, attributes: Dict, ts: Optional[Any] = None) -
489489
:param attributes: Dictionary passed by the Robot Framework
490490
:param ts: Timestamp(used by the ResultVisitor)
491491
"""
492-
kwd = Keyword(name=name, parent_type=self.current_item.type, robot_attributes=attributes)
492+
kwd = Keyword(name, attributes, self.current_item)
493493
parent = self.current_item
494-
kwd.rp_parent_item_id = parent.rp_item_id
495494
skip_kwd = parent.remove_data
496495
skip_data = self._remove_all_keyword_content or self._remove_data_passed_tests
497496
kwd.remove_data = skip_kwd or skip_data
@@ -535,31 +534,33 @@ def end_keyword(self, _: Optional[str], attributes: Dict, ts: Optional[Any] = No
535534
kwd = self.current_item.update(attributes)
536535

537536
if kwd.matched_filter is WUKS_KEYWORD_MATCH and kwd.skip_origin is kwd:
538-
skipped_kwds = kwd.skipped_keywords
539-
skipped_kwds_num = len(skipped_kwds)
540-
if skipped_kwds_num > 2:
537+
skipped_keywords = kwd.skipped_keywords
538+
skipped_keywords_num = len(skipped_keywords)
539+
if skipped_keywords_num > 2:
541540
if kwd.status == "FAIL":
542541
message = REMOVED_WUKS_KEYWORD_LOG.format(number=len(kwd.skipped_keywords) - 1)
543542
else:
544543
message = REMOVED_WUKS_KEYWORD_LOG.format(number=len(kwd.skipped_keywords) - 2)
545544
self._log_data_removed(kwd.rp_item_id, kwd.start_time, message)
546-
if skipped_kwds_num > 1 and kwd.status != "FAIL":
545+
if skipped_keywords_num > 1 and kwd.status != "FAIL":
547546
first_iteration = kwd.skipped_keywords[0]
548547
self._post_skipped_keywords(first_iteration)
549548
self._do_end_keyword(first_iteration)
550-
if skipped_kwds_num > 0:
549+
if skipped_keywords_num > 0:
551550
last_iteration = kwd.skipped_keywords[-1]
552551
self._post_skipped_keywords(last_iteration)
553552
self._do_end_keyword(last_iteration, ts)
554553

555554
elif (
556555
(kwd.matched_filter is FOR_KEYWORD_MATCH) or (kwd.matched_filter is WHILE_KEYWORD_NAME)
557556
) and kwd.skip_origin is kwd:
558-
skipped_kwds = kwd.skipped_keywords
559-
skipped_kwds_num = len(skipped_kwds)
560-
if skipped_kwds_num > 1:
557+
skipped_keywords = kwd.skipped_keywords
558+
skipped_keywords_num = len(skipped_keywords)
559+
if skipped_keywords_num > 1:
561560
self._log_data_removed(
562-
kwd.rp_item_id, kwd.start_time, REMOVED_FOR_WHILE_KEYWORD_LOG.format(number=skipped_kwds_num - 1)
561+
kwd.rp_item_id,
562+
kwd.start_time,
563+
REMOVED_FOR_WHILE_KEYWORD_LOG.format(number=skipped_keywords_num - 1),
563564
)
564565
last_iteration = kwd.skipped_keywords[-1]
565566
self._post_skipped_keywords(last_iteration)

robotframework_reportportal/model.py

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,31 @@
2424
TEST_CASE_ID_SIGN = "test_case_id:"
2525

2626

27+
class Entity:
28+
"""Base class for all test items."""
29+
30+
type: str
31+
remove_data: bool
32+
rp_item_id: Optional[str]
33+
parent: Optional["Entity"]
34+
35+
def __init__(self, entity_type: str, parent: Optional["Entity"]):
36+
"""Initialize required attributes.
37+
38+
:param entity_type: Type of the entity
39+
:param parent: Parent entity
40+
"""
41+
self.type = entity_type
42+
self.parent = parent
43+
self.rp_item_id = None
44+
self.remove_data = False
45+
46+
@property
47+
def rp_parent_item_id(self):
48+
"""Get parent item ID."""
49+
return getattr(self.parent, "rp_item_id", None)
50+
51+
2752
class LogMessage(str):
2853
"""Class represents Robot Framework messages."""
2954

@@ -44,7 +69,7 @@ def __init__(self, message: str):
4469
self.timestamp = None
4570

4671

47-
class Keyword:
72+
class Keyword(Entity):
4873
"""Class represents Robot Framework keyword."""
4974

5075
robot_attributes: Dict[str, Any]
@@ -56,9 +81,6 @@ class Keyword:
5681
keyword_type: str
5782
libname: str
5883
name: str
59-
rp_item_id: Optional[str]
60-
rp_parent_item_id: Optional[str]
61-
parent_type: str
6284
start_time: str
6385
status: str
6486
tags: List[str]
@@ -70,13 +92,14 @@ class Keyword:
7092
matched_filter: Optional[Any]
7193
skip_origin: Optional[Any]
7294

73-
def __init__(self, name: str, robot_attributes: Dict[str, Any], parent_type: Optional[str] = None):
95+
def __init__(self, name: str, robot_attributes: Dict[str, Any], parent: Entity):
7496
"""Initialize required attributes.
7597
7698
:param name: Name of the keyword
7799
:param robot_attributes: Attributes passed through the listener
78-
:param parent_type: Type of the parent test item
100+
:param parent: Parent entity
79101
"""
102+
super().__init__("KEYWORD", parent)
80103
self.robot_attributes = robot_attributes
81104
self.args = robot_attributes["args"]
82105
self.assign = robot_attributes["assign"]
@@ -86,17 +109,13 @@ def __init__(self, name: str, robot_attributes: Dict[str, Any], parent_type: Opt
86109
self.keyword_type = robot_attributes["type"]
87110
self.libname = robot_attributes["libname"]
88111
self.name = name
89-
self.rp_item_id = None
90-
self.rp_parent_item_id = None
91-
self.parent_type = parent_type
92112
self.start_time = robot_attributes["starttime"]
93113
self.status = robot_attributes.get("status")
94114
self.tags = robot_attributes["tags"]
95115
self.type = "KEYWORD"
96116
self.skipped_keywords = []
97117
self.skipped_logs = []
98118
self.posted = True
99-
self.remove_data = False
100119
self.matched_filter = None
101120
self.skip_origin = None
102121

@@ -111,12 +130,12 @@ def get_name(self) -> str:
111130
def get_type(self) -> str:
112131
"""Get keyword type."""
113132
if self.keyword_type.lower() in ("setup", "teardown"):
114-
if self.parent_type.lower() == "keyword":
133+
if self.parent.type.lower() == "keyword":
115134
return "STEP"
116135
if self.keyword_type.lower() == "setup":
117-
return "BEFORE_{0}".format(self.parent_type.upper())
136+
return "BEFORE_{0}".format(self.parent.type.upper())
118137
if self.keyword_type.lower() == "teardown":
119-
return "AFTER_{0}".format(self.parent_type.upper())
138+
return "AFTER_{0}".format(self.parent.type.upper())
120139
else:
121140
return "STEP"
122141

@@ -130,7 +149,7 @@ def update(self, attributes: Dict[str, Any]) -> "Keyword":
130149
return self
131150

132151

133-
class Suite:
152+
class Suite(Entity):
134153
"""Class represents Robot Framework test suite."""
135154

136155
robot_attributes: Union[List[str], Dict[str, Any]]
@@ -141,23 +160,21 @@ class Suite:
141160
metadata: Dict[str, str]
142161
name: str
143162
robot_id: str
144-
rp_item_id: Optional[str]
145-
rp_parent_item_id: Optional[str]
146163
start_time: Optional[str]
147164
statistics: str
148165
status: str
149166
suites: List[str]
150167
tests: List[str]
151168
total_tests: int
152-
type: str = "SUITE"
153-
remove_data: bool = False
154169

155-
def __init__(self, name: str, robot_attributes: Dict[str, Any]):
170+
def __init__(self, name: str, robot_attributes: Dict[str, Any], parent: Optional[Entity] = None):
156171
"""Initialize required attributes.
157172
158173
:param name: Suite name
159174
:param robot_attributes: Suite attributes passed through the listener
175+
:param parent: Parent entity
160176
"""
177+
super().__init__("SUITE", parent)
161178
self.robot_attributes = robot_attributes
162179
self.doc = robot_markup_to_markdown(robot_attributes["doc"])
163180
self.end_time = robot_attributes.get("endtime", "")
@@ -166,15 +183,12 @@ def __init__(self, name: str, robot_attributes: Dict[str, Any]):
166183
self.metadata = robot_attributes["metadata"]
167184
self.name = name
168185
self.robot_id = robot_attributes["id"]
169-
self.rp_item_id = None
170-
self.rp_parent_item_id = None
171186
self.start_time = robot_attributes.get("starttime")
172187
self.statistics = robot_attributes.get("statistics")
173188
self.status = robot_attributes.get("status")
174189
self.suites = robot_attributes["suites"]
175190
self.tests = robot_attributes["tests"]
176191
self.total_tests = robot_attributes["totaltests"]
177-
self.type = "SUITE"
178192

179193
@property
180194
def attributes(self) -> Optional[List[Dict[str, str]]]:
@@ -224,7 +238,7 @@ def attributes(self) -> Optional[List[Dict[str, str]]]:
224238
return self.launch_attributes
225239

226240

227-
class Test:
241+
class Test(Entity):
228242
"""Class represents Robot Framework test case."""
229243

230244
_critical: str
@@ -237,21 +251,18 @@ class Test:
237251
message: str
238252
name: str
239253
robot_id: str
240-
rp_item_id: Optional[str]
241-
rp_parent_item_id: Optional[str]
242254
start_time: str
243255
status: str
244256
template: str
245-
type: str = "TEST"
246257
skipped_keywords: List[Keyword]
247-
remove_data: bool = False
248258

249-
def __init__(self, name: str, robot_attributes: Dict[str, Any], test_attributes: List[str]):
259+
def __init__(self, name: str, robot_attributes: Dict[str, Any], test_attributes: List[str], parent: Entity):
250260
"""Initialize required attributes.
251261
252262
:param name: Name of the test
253263
:param robot_attributes: Attributes passed through the listener
254264
"""
265+
super().__init__("TEST", parent)
255266
# for backward compatibility with Robot < 4.0 mark every test case
256267
# as critical if not set
257268
self._critical = robot_attributes.get("critical", "yes")
@@ -264,12 +275,9 @@ def __init__(self, name: str, robot_attributes: Dict[str, Any], test_attributes:
264275
self.message = robot_attributes.get("message")
265276
self.name = name
266277
self.robot_id = robot_attributes["id"]
267-
self.rp_item_id = None
268-
self.rp_parent_item_id = None
269278
self.start_time = robot_attributes["starttime"]
270279
self.status = robot_attributes.get("status")
271280
self.template = robot_attributes["template"]
272-
self.type = "TEST"
273281
self.skipped_keywords = []
274282

275283
@property

robotframework_reportportal/service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ def log(self, message: LogMessage, ts: Optional[str] = None):
269269
"""
270270
sl_rq = {
271271
"attachment": message.attachment,
272-
"item_id": message.item_id,
272+
"item_id": None if message.launch_log else message.item_id,
273273
"level": LOG_LEVEL_MAPPING.get(message.level, "INFO"),
274274
"message": message.message,
275275
"time": ts or to_epoch(message.timestamp) or timestamp(),

0 commit comments

Comments
 (0)