@@ -83,4 +83,56 @@ def test_break_stops_at_the_extra_file
8383 end
8484 end
8585 end
86+
87+ class NestedBreakTest < ProtocolTestCase
88+ PROGRAM = <<~RUBY
89+ 1| def foo(x)
90+ 2| x
91+ 3| end
92+ 4|
93+ 5| foo("foo")
94+ RUBY
95+
96+ def test_breakpoint_can_be_triggered_inside_suspenssion
97+ run_protocol_scenario PROGRAM , cdp : false do
98+ req_add_breakpoint 2
99+ req_continue
100+ assert_line_num 2
101+
102+ assert_locals_result (
103+ [
104+ { name : "%self" , value : "main" , type : "Object" } ,
105+ { name : "x" , value : "foo" , type : "String" } ,
106+ ]
107+ )
108+
109+ # Only if TracePoint.allow_reentry is available, we can trigger TracePoint events
110+ # inside another TracePoint event, which is essential for nested breakpoints.
111+ if TracePoint . respond_to? :allow_reentry
112+ evaluate ( "foo('bar')" )
113+
114+ assert_line_num 2
115+ assert_locals_result (
116+ [
117+ { name : "%self" , value : "main" , type : "Object" } ,
118+ { name : "x" , value : "bar" , type : "String" } ,
119+ ]
120+ )
121+ end
122+
123+ req_terminate_debuggee
124+ end
125+ end
126+
127+ private
128+
129+ def evaluate ( expression )
130+ res = send_dap_request 'stackTrace' ,
131+ threadId : 1 ,
132+ startFrame : 0 ,
133+ levels : 20
134+ f_id = res . dig ( :body , :stackFrames , 0 , :id )
135+ send_request 'evaluate' , expression : expression , frameId : f_id , context : "repl"
136+ end
137+ end
86138end
0 commit comments