11import os
22import time
3- from typing import Optional
3+ from typing import Optional , Callable , Any , List , Union
44import uuid
55
66import dap_server
@@ -67,7 +67,10 @@ def set_source_breakpoints_assembly(
6767 self , source_reference , lines , data = None , wait_for_resolve = True
6868 ):
6969 return self .set_source_breakpoints_from_source (
70- Source (source_reference = source_reference ), lines , data , wait_for_resolve
70+ Source .build (source_reference = source_reference ),
71+ lines ,
72+ data ,
73+ wait_for_resolve ,
7174 )
7275
7376 def set_source_breakpoints_from_source (
@@ -120,11 +123,19 @@ def wait_for_breakpoints_to_resolve(
120123 f"Expected to resolve all breakpoints. Unresolved breakpoint ids: { unresolved_breakpoints } " ,
121124 )
122125
123- def waitUntil (self , condition_callback ):
124- for _ in range (20 ):
125- if condition_callback ():
126+ def wait_until (
127+ self ,
128+ predicate : Callable [[], bool ],
129+ delay : float = 0.5 ,
130+ timeout : float = DEFAULT_TIMEOUT ,
131+ ) -> bool :
132+ """Repeatedly run the predicate until either the predicate returns True
133+ or a timeout has occurred."""
134+ deadline = time .monotonic () + timeout
135+ while deadline > time .monotonic ():
136+ if predicate ():
126137 return True
127- time .sleep (0.5 )
138+ time .sleep (delay )
128139 return False
129140
130141 def assertCapabilityIsSet (self , key : str , msg : Optional [str ] = None ) -> None :
@@ -137,13 +148,16 @@ def assertCapabilityIsNotSet(self, key: str, msg: Optional[str] = None) -> None:
137148 if key in self .dap_server .capabilities :
138149 self .assertEqual (self .dap_server .capabilities [key ], False , msg )
139150
140- def verify_breakpoint_hit (self , breakpoint_ids , timeout = DEFAULT_TIMEOUT ):
151+ def verify_breakpoint_hit (
152+ self , breakpoint_ids : List [Union [int , str ]], timeout : float = DEFAULT_TIMEOUT
153+ ):
141154 """Wait for the process we are debugging to stop, and verify we hit
142155 any breakpoint location in the "breakpoint_ids" array.
143156 "breakpoint_ids" should be a list of breakpoint ID strings
144157 (["1", "2"]). The return value from self.set_source_breakpoints()
145158 or self.set_function_breakpoints() can be passed to this function"""
146159 stopped_events = self .dap_server .wait_for_stopped (timeout )
160+ normalized_bp_ids = [str (b ) for b in breakpoint_ids ]
147161 for stopped_event in stopped_events :
148162 if "body" in stopped_event :
149163 body = stopped_event ["body" ]
@@ -154,22 +168,16 @@ def verify_breakpoint_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
154168 and body ["reason" ] != "instruction breakpoint"
155169 ):
156170 continue
157- if "description " not in body :
171+ if "hitBreakpointIds " not in body :
158172 continue
159- # Descriptions for breakpoints will be in the form
160- # "breakpoint 1.1", so look for any description that matches
161- # ("breakpoint 1.") in the description field as verification
162- # that one of the breakpoint locations was hit. DAP doesn't
163- # allow breakpoints to have multiple locations, but LLDB does.
164- # So when looking at the description we just want to make sure
165- # the right breakpoint matches and not worry about the actual
166- # location.
167- description = body ["description" ]
168- for breakpoint_id in breakpoint_ids :
169- match_desc = f"breakpoint { breakpoint_id } ."
170- if match_desc in description :
173+ hit_breakpoint_ids = body ["hitBreakpointIds" ]
174+ for bp in hit_breakpoint_ids :
175+ if str (bp ) in normalized_bp_ids :
171176 return
172- self .assertTrue (False , f"breakpoint not hit, stopped_events={ stopped_events } " )
177+ self .assertTrue (
178+ False ,
179+ f"breakpoint not hit, wanted breakpoint_ids { breakpoint_ids } in stopped_events { stopped_events } " ,
180+ )
173181
174182 def verify_all_breakpoints_hit (self , breakpoint_ids , timeout = DEFAULT_TIMEOUT ):
175183 """Wait for the process we are debugging to stop, and verify we hit
@@ -213,7 +221,7 @@ def verify_stop_exception_info(self, expected_description, timeout=DEFAULT_TIMEO
213221 return True
214222 return False
215223
216- def verify_commands (self , flavor , output , commands ):
224+ def verify_commands (self , flavor : str , output : str , commands : list [ str ] ):
217225 self .assertTrue (output and len (output ) > 0 , "expect console output" )
218226 lines = output .splitlines ()
219227 prefix = "(lldb) "
@@ -226,10 +234,11 @@ def verify_commands(self, flavor, output, commands):
226234 found = True
227235 break
228236 self .assertTrue (
229- found , "verify '%s' found in console output for '%s'" % (cmd , flavor )
237+ found ,
238+ f"Command '{ flavor } ' - '{ cmd } ' not found in output: { output } " ,
230239 )
231240
232- def get_dict_value (self , d , key_path ) :
241+ def get_dict_value (self , d : dict , key_path : list [ str ]) -> Any :
233242 """Verify each key in the key_path array is in contained in each
234243 dictionary within "d". Assert if any key isn't in the
235244 corresponding dictionary. This is handy for grabbing values from VS
@@ -298,28 +307,34 @@ def get_source_and_line(self, threadId=None, frameIndex=0):
298307 return (source ["path" ], stackFrame ["line" ])
299308 return ("" , 0 )
300309
301- def get_stdout (self , timeout = 0.0 ):
302- return self .dap_server .get_output ("stdout" , timeout = timeout )
310+ def get_stdout (self ):
311+ return self .dap_server .get_output ("stdout" )
303312
304- def get_console (self , timeout = 0.0 ):
305- return self .dap_server .get_output ("console" , timeout = timeout )
313+ def get_console (self ):
314+ return self .dap_server .get_output ("console" )
306315
307- def get_important (self , timeout = 0.0 ):
308- return self .dap_server .get_output ("important" , timeout = timeout )
316+ def get_important (self ):
317+ return self .dap_server .get_output ("important" )
309318
310- def collect_stdout (self , timeout_secs , pattern = None ):
319+ def collect_stdout (
320+ self , timeout : float = DEFAULT_TIMEOUT , pattern : Optional [str ] = None
321+ ) -> str :
311322 return self .dap_server .collect_output (
312- "stdout" , timeout_secs = timeout_secs , pattern = pattern
323+ "stdout" , timeout = timeout , pattern = pattern
313324 )
314325
315- def collect_console (self , timeout_secs , pattern = None ):
326+ def collect_console (
327+ self , timeout : float = DEFAULT_TIMEOUT , pattern : Optional [str ] = None
328+ ) -> str :
316329 return self .dap_server .collect_output (
317- "console" , timeout_secs = timeout_secs , pattern = pattern
330+ "console" , timeout = timeout , pattern = pattern
318331 )
319332
320- def collect_important (self , timeout_secs , pattern = None ):
333+ def collect_important (
334+ self , timeout : float = DEFAULT_TIMEOUT , pattern : Optional [str ] = None
335+ ) -> str :
321336 return self .dap_server .collect_output (
322- "important" , timeout_secs = timeout_secs , pattern = pattern
337+ "important" , timeout = timeout , pattern = pattern
323338 )
324339
325340 def get_local_as_int (self , name , threadId = None ):
0 commit comments