@@ -212,7 +212,11 @@ def __new__(cls, *args: Any, **kwargs: Any) -> Any:
212
212
213
213
def __init__ (self ) -> None :
214
214
self .breakpoints : Dict [str , BreakpointsEntry ] = {}
215
+
215
216
self .exception_breakpoints : Set [ExceptionBreakpointsEntry ] = set ()
217
+ self .exception_breakpoints .add (
218
+ ExceptionBreakpointsEntry ((), (ExceptionFilterOptions ("uncaughted_failed_keyword" ),), ())
219
+ )
216
220
217
221
self .main_thread : Optional [threading .Thread ] = None
218
222
self .full_stack_frames : Deque [StackFrameEntry ] = deque ()
@@ -528,14 +532,14 @@ def process_start_state(self, source: str, line_no: int, type: str, status: str)
528
532
),
529
533
)
530
534
531
- def process_end_state (self , status : str , filter_id : str , description : str , text : Optional [str ]) -> None :
535
+ def process_end_state (self , status : str , filter_id : Set [ str ] , description : str , text : Optional [str ]) -> None :
532
536
if (
533
537
not self .terminated
534
538
and status == "FAIL"
535
539
and any (
536
540
v
537
541
for v in self .exception_breakpoints
538
- if v .filter_options is not None and any (o for o in v .filter_options if o .filter_id == filter_id )
542
+ if v .filter_options and any (o for o in v .filter_options if o .filter_id in filter_id )
539
543
)
540
544
):
541
545
self .state = State .Paused
@@ -633,6 +637,11 @@ def add_stackframe_entry(
633
637
longname = longname ,
634
638
)
635
639
640
+ self .full_stack_frames .appendleft (result )
641
+
642
+ if type in ["KEYWORD" ] and source is None and line is None and column is None :
643
+ return result
644
+
636
645
if type in ["SUITE" , "TEST" ]:
637
646
self .stack_frames .appendleft (result )
638
647
elif type in ["KEYWORD" , "SETUP" , "TEARDOWN" ] and isinstance (handler , UserKeywordHandler ):
@@ -644,8 +653,6 @@ def add_stackframe_entry(
644
653
if self .stack_frames :
645
654
self .stack_frames [0 ].stack_frames .appendleft (result )
646
655
647
- self .full_stack_frames .appendleft (result )
648
-
649
656
return result
650
657
651
658
def remove_stackframe_entry (
@@ -654,12 +661,17 @@ def remove_stackframe_entry(
654
661
type : str ,
655
662
source : Optional [str ],
656
663
line : Optional [int ],
657
- column : Optional [int ] = 1 ,
664
+ column : Optional [int ] = None ,
658
665
* ,
659
666
handler : Any = None ,
660
667
) -> None :
661
668
from robot .running .userkeyword import UserKeywordHandler
662
669
670
+ self .full_stack_frames .popleft ()
671
+
672
+ if type in ["KEYWORD" ] and source is None and line is None and column is None :
673
+ return
674
+
663
675
if type in ["SUITE" , "TEST" ]:
664
676
self .stack_frames .popleft ()
665
677
elif type in ["KEYWORD" , "SETUP" , "TEARDOWN" ] and isinstance (handler , UserKeywordHandler ):
@@ -671,8 +683,6 @@ def remove_stackframe_entry(
671
683
if self .stack_frames :
672
684
self .stack_frames [0 ].stack_frames .popleft ()
673
685
674
- self .full_stack_frames .popleft ()
675
-
676
686
def start_suite (self , name : str , attributes : Dict [str , Any ]) -> None :
677
687
source = attributes .get ("source" , None )
678
688
line_no = attributes .get ("lineno" , 1 )
@@ -707,12 +717,13 @@ def end_suite(self, name: str, attributes: Dict[str, Any]) -> None:
707
717
if self .debug :
708
718
status = attributes .get ("status" , "" )
709
719
710
- self .process_end_state (
711
- status ,
712
- "failed_suite" ,
713
- "Suite failed." ,
714
- f"Suite failed{ f': { v } ' if (v := attributes .get ('message' , None )) else '' } " ,
715
- )
720
+ if status == "FAIL" :
721
+ self .process_end_state (
722
+ status ,
723
+ {"failed_suite" },
724
+ "Suite failed." ,
725
+ f"Suite failed{ f': { v } ' if (v := attributes .get ('message' , None )) else '' } " ,
726
+ )
716
727
717
728
source = attributes .get ("source" , None )
718
729
line_no = attributes .get ("lineno" , 1 )
@@ -739,12 +750,13 @@ def end_test(self, name: str, attributes: Dict[str, Any]) -> None:
739
750
if self .debug :
740
751
status = attributes .get ("status" , "" )
741
752
742
- self .process_end_state (
743
- status ,
744
- "failed_test" ,
745
- "Test failed." ,
746
- f"Test failed{ f': { v } ' if (v := attributes .get ('message' , None )) else '' } " ,
747
- )
753
+ if status == "FAIL" :
754
+ self .process_end_state (
755
+ status ,
756
+ {"failed_test" },
757
+ "Test failed." ,
758
+ f"Test failed{ f': { v } ' if (v := attributes .get ('message' , None )) else '' } " ,
759
+ )
748
760
749
761
source = attributes .get ("source" , None )
750
762
line_no = attributes .get ("lineno" , 1 )
@@ -784,17 +796,36 @@ def start_keyword(self, name: str, attributes: Dict[str, Any]) -> None:
784
796
785
797
self .wait_for_running ()
786
798
799
+ CAUGHTED_KEYWORDS = [
800
+ "BuiltIn.Run Keyword And Expect Error" ,
801
+ "BuiltIn.Run Keyword And Ignore Error" ,
802
+ "BuiltIn.Run Keyword And Warn On Failure" ,
803
+ "BuiltIn.Wait Until Keyword Succeeds" ,
804
+ "BuiltIn.Run Keyword And Continue On Failure" ,
805
+ ]
806
+
807
+ def in_caughted_keyword (self ) -> bool :
808
+ r = next (
809
+ (
810
+ v
811
+ for v in itertools .islice (self .full_stack_frames , 1 , None )
812
+ if v .type == "KEYWORD" and v .longname in self .CAUGHTED_KEYWORDS
813
+ ),
814
+ None ,
815
+ )
816
+ return r is None
817
+
787
818
def end_keyword (self , name : str , attributes : Dict [str , Any ]) -> None :
788
819
from robot .running .context import EXECUTION_CONTEXTS
789
820
790
821
type = attributes .get ("type" , None )
791
822
if self .debug :
792
823
status = attributes .get ("status" , "" )
793
824
794
- if status != "NOT RUN " and type in ["KEYWORD" , "SETUP" , "TEARDOWN" ]:
825
+ if status == "FAIL " and type in ["KEYWORD" , "SETUP" , "TEARDOWN" ]:
795
826
self .process_end_state (
796
827
status ,
797
- "failed_keyword" ,
828
+ { "failed_keyword" , * ({ "uncaughted_failed_keyword" } if self . in_caughted_keyword () else {})} ,
798
829
"Keyword failed." ,
799
830
f"Keyword failed: { self .last_fail_message } " if self .last_fail_message else "Keyword failed." ,
800
831
)
@@ -1149,7 +1180,7 @@ def set_exception_breakpoints(
1149
1180
1150
1181
if filter_options is not None :
1151
1182
for option in filter_options :
1152
- if option .filter_id in ["failed_keyword" , "failed_test" , "failed_suite" ]:
1183
+ if option .filter_id in ["failed_keyword" , "uncaughted_failed_keyword" , " failed_test" , "failed_suite" ]:
1153
1184
entry = ExceptionBreakpointsEntry (
1154
1185
tuple (filters ),
1155
1186
tuple (filter_options ) if filter_options is not None else None ,
0 commit comments