def run_test_reattach(self, debug_info): options = {'debugOptions': ['RedirectOutput']} with self.start_debugging(debug_info) as dbg: session = dbg.session stopped1 = session.get_awaiter_for_event('stopped') (_, req_launch_attach, _, _, _, _) = lifecycle_handshake( session, debug_info.starttype, options=options, threads=True) Awaitable.wait_all(req_launch_attach, stopped1) thread_id = stopped1.event.body['threadId'] req_disconnect = session.send_request('disconnect', restart=False) req_disconnect.wait() time.sleep(1) (_, req_launch_attach, _, _, _, _) = lifecycle_handshake( session, debug_info.starttype, options=options, threads=True) Awaitable.wait_all(req_launch_attach) self.set_var_to_end_loop(session, thread_id) session.send_request('continue', threadId=thread_id) received = list(_strip_newline_output_events(session.received)) self.assert_contains(received, [ self.new_event('exited', exitCode=0), self.new_event('terminated'), ])
def run_test_breaking_into_unhandled_exceptions(self, debug_info, expected_source_name): excbreakpoints = [{'filters': ['uncaught']}] options = {'debugOptions': ['RedirectOutput']} with self.start_debugging(debug_info) as dbg: stopped = dbg.session.get_awaiter_for_event('stopped') (_, req_launch_attach, _, _, _, _) = lifecycle_handshake( dbg.session, debug_info.starttype, excbreakpoints=excbreakpoints, options=options, threads=True) req_launch_attach.wait(timeout=3.0) stopped.wait(timeout=3.0) self.assertEqual(stopped.event.body['text'], 'ArithmeticError') self.assertEqual(stopped.event.body['description'], 'Hello') thread_id = stopped.event.body['threadId'] req_exc_info = dbg.session.send_request( 'exceptionInfo', threadId=thread_id, ) req_exc_info.wait() exc_info = req_exc_info.resp.body self.assert_is_subset( exc_info, { 'exceptionId': 'ArithmeticError', 'breakMode': 'unhandled', 'details': { 'typeName': 'ArithmeticError', 'source': expected_source_name } }) continued = dbg.session.get_awaiter_for_event('continued') dbg.session.send_request( 'continue', threadId=thread_id, ).wait() Awaitable.wait_all(continued) received = list(_strip_newline_output_events(dbg.session.received)) # TODO: Re-enable after fixing #685 #self.assertEqual( # len(self.find_events(received, 'output', {'category': 'stdout'})), # 1) std_errs = self.find_events(received, 'output', {'category': 'stderr'}) self.assertGreaterEqual(len(std_errs), 1) std_err_msg = ''.join([msg.body['output'] for msg in std_errs]) self.assertIn('ArithmeticError: Hello', std_err_msg) self.assert_contains(received, [ self.new_event('continued', threadId=thread_id), # TODO: Re-enable after fixing #685 # self.new_event('output', category='stdout', output='one'), self.new_event('exited', exitCode=0), self.new_event('terminated'), ])
def test_attach_embedded(self): lockfile = self.workspace.lockfile() done, waitscript = lockfile.wait_in_script() addr = Address('localhost', 8888) script = dedent(""" from __future__ import print_function import sys sys.path.insert(0, {!r}) import ptvsd ptvsd.enable_attach({}, redirect_output={}) ptvsd.wait_for_attach() print('success!', end='') %s """).format(os.getcwd(), tuple(addr), True) filename = self.write_script('spam.py', script % waitscript) with DebugAdapter.start_embedded(addr, filename) as adapter: with DebugClient() as editor: session = editor.attach_socket(addr, adapter) (req_initialize, req_launch, req_config, _, _, _) = lifecycle_handshake(session, 'attach') Awaitable.wait_all(req_initialize, req_launch) done() adapter.wait() for i in range(10): # It could take some additional time for the adapter # to actually get the success output, so, wait for the # expected condition in a busy loop. out = adapter.output.decode('utf-8') if 'success!' in out: break import time time.sleep(.1) received = list(_strip_newline_output_events(session.received)) self.assert_contains(received, [ self.new_version_event(session.received), self.new_response(req_initialize.req, **INITIALIZE_RESPONSE), self.new_event('initialized'), self.new_response(req_launch.req), self.new_response(req_config.req), self.new_event( 'process', **{ 'isLocalProcess': True, 'systemProcessId': adapter.pid, 'startMethod': 'attach', 'name': filename, }), self.new_event('output', output='success!', category='stdout'), self.new_event('exited', exitCode=0), self.new_event('terminated'), ]) self.assertIn('success!', out)
def run_test_breaking_into_handled_exceptions(self, debug_info): excbreakpoints = [{'filters': ['raised', 'uncaught']}] options = {'debugOptions': ['RedirectOutput']} with self.start_debugging(debug_info) as dbg: stopped = dbg.session.get_awaiter_for_event('stopped') (_, req_launch_attach, _, _, _, _) = lifecycle_handshake(dbg.session, debug_info.starttype, excbreakpoints=excbreakpoints, options=options, threads=True) Awaitable.wait_all(req_launch_attach, stopped) self.assertEqual(stopped.event.body['text'], 'ArithmeticError') self.assertIn("ArithmeticError('Hello'", stopped.event.body['description']) thread_id = stopped.event.body['threadId'] req_exc_info = dbg.session.send_request( 'exceptionInfo', threadId=thread_id, ) req_exc_info.wait() exc_info = req_exc_info.resp.body self.assert_is_subset( exc_info, { 'exceptionId': 'ArithmeticError', 'breakMode': 'always', 'details': { 'typeName': 'ArithmeticError', # 'source': debug_info.filename } }) continued = dbg.session.get_awaiter_for_event('continued') dbg.session.send_request( 'continue', threadId=thread_id, ).wait() Awaitable.wait_all(continued) received = list(_strip_newline_output_events(dbg.session.received)) self.assert_contains(received, [ self.new_event('continued', threadId=thread_id), self.new_event('output', category='stdout', output='end'), self.new_event('exited', exitCode=0), self.new_event('terminated'), ])
def run_test_breaking_into_raised_exceptions_only(self, debug_info, expected_source_name): # NOTE: for this case we will be using a unhandled exception. The # behavior expected here is that it breaks once when the exception # was raised but not during postmortem excbreakpoints = [{'filters': ['raised']}] options = {'debugOptions': ['RedirectOutput']} with self.start_debugging(debug_info) as dbg: stopped = dbg.session.get_awaiter_for_event('stopped') (_, req_launch_attach, _, _, _, _) = lifecycle_handshake(dbg.session, debug_info.starttype, excbreakpoints=excbreakpoints, options=options) Awaitable.wait_all(req_launch_attach, stopped) self.assertEqual(stopped.event.body['text'], 'ArithmeticError') self.assertEqual(stopped.event.body['description'], 'Hello') thread_id = stopped.event.body['threadId'] req_exc_info = dbg.session.send_request('exceptionInfo', threadId=thread_id) req_exc_info.wait() exc_info = req_exc_info.resp.body self.assert_is_subset( exc_info, { 'exceptionId': 'ArithmeticError', 'breakMode': 'always', 'details': { 'typeName': 'ArithmeticError', 'source': expected_source_name } }) continued = dbg.session.get_awaiter_for_event('continued') dbg.session.send_request( 'continue', threadId=thread_id, ).wait() Awaitable.wait_all(continued) received = list(_strip_newline_output_events(dbg.session.received)) self.assert_contains(received, [ self.new_event('continued', threadId=thread_id), self.new_event('exited', exitCode=0), self.new_event('terminated'), ])
def run_test_termination(self, debug_info): with self.start_debugging(debug_info) as dbg: session = dbg.session exited = session.get_awaiter_for_event('exited') terminated = session.get_awaiter_for_event('terminated') (_, req_launch, _, _, _, _) = lifecycle_handshake(dbg.session, debug_info.starttype, threads=True) Awaitable.wait_all(req_launch, session.get_awaiter_for_event('thread')) disconnect = session.send_request('disconnect') Awaitable.wait_all(exited, terminated, disconnect)
def run_test_attach_or_launch(self, debug_info, end_loop=False): options = {'debugOptions': ['RedirectOutput']} with self.start_debugging(debug_info) as dbg: session = dbg.session stopped = session.get_awaiter_for_event('stopped') (_, req_launch_attach, _, _, _, _) = lifecycle_handshake( session, debug_info.starttype, options=options) Awaitable.wait_all(req_launch_attach, stopped) thread_id = stopped.event.body['threadId'] if end_loop: self.set_var_to_end_loop(session, thread_id) session.send_request('continue', threadId=thread_id) received = list(_strip_newline_output_events(dbg.session.received)) self.assert_contains(received, [ self.new_event('output', category='stdout', output='one'), self.new_event('output', category='stdout', output='two'), self.new_event('continued', threadId=thread_id), self.new_event('exited', exitCode=0), self.new_event('terminated'), ])
def run_test_with_unhandled_exception(self, debug_info, framework, expected_source_name): if (debug_info.starttype == 'attach'): pathMappings = [] pathMappings.append({ 'localRoot': debug_info.cwd, 'remoteRoot': debug_info.cwd }) options = { 'debugOptions': ['RedirectOutput', framework], 'pathMappings': pathMappings } else: options = {'debugOptions': ['RedirectOutput', framework]} excbreakpoints = [{'filters': ['raised', 'uncaught']}] with self.start_debugging(debug_info) as dbg: session = dbg.session with session.wait_for_event('stopped') as result: ( _, req_launch_attach, _, _, _, _, ) = lifecycle_handshake(session, debug_info.starttype, options=options, excbreakpoints=excbreakpoints) req_launch_attach.wait() # wait for flask web server start count = 0 base_path = None while base_path is None and count < 10: outevent = session.get_awaiter_for_event('output') Awaitable.wait_all(outevent) events = self.find_events(session.received, 'output') count += 1 for e in events: matches = re.findall(re_link, e.body['output']) if len(matches) > 0 and len(matches[0]) > 0 and \ len(matches[0][0].strip()) > 0: base_path = matches[0][0] break # connect to web server path = base_path + \ 'unhandled' if base_path.endswith('/') else '/unhandled' web_result = {} web_client_thread = threading.Thread( target=get_web_string_no_error, args=(path, web_result), name='test.webClient') web_client_thread.start() event = result['msg'] thread_id = event.body['threadId'] req_exc_info = dbg.session.send_request( 'exceptionInfo', threadId=thread_id, ) req_exc_info.wait() exc_info = req_exc_info.resp.body self.assert_is_subset( exc_info, { 'exceptionId': 'ArithmeticError', 'breakMode': 'always', 'details': { 'typeName': 'ArithmeticError', 'source': expected_source_name } }) continued = dbg.session.get_awaiter_for_event('continued') dbg.session.send_request( 'continue', threadId=thread_id, ).wait() Awaitable.wait_all(continued) # Shutdown webserver path = base_path + 'exit' if base_path.endswith('/') else '/exit' web_client_thread = threading.Thread( target=get_web_string_no_error, args=(path, None), name='test.webClient.shutdown') web_client_thread.start() web_client_thread.join(timeout=1) received = list(_strip_newline_output_events(dbg.session.received)) if framework != 'Django': # TODO: Figure out better way to shutdown Django self.assert_contains(received, [ self.new_event('exited', exitCode=0), self.new_event('terminated'), ])
def run_test_with_break_points(self, debug_info, **kwargs): bp_filename = kwargs.pop('bp_filename') bp_line = kwargs.pop('bp_line') bp_name = kwargs.pop('bp_name') bp_var_value = kwargs.pop('bp_var_value') framework = kwargs.pop('framework', 'Django') if (debug_info.starttype == 'attach'): pathMappings = [] pathMappings.append({ 'localRoot': debug_info.cwd, 'remoteRoot': debug_info.cwd }) options = { 'debugOptions': ['RedirectOutput', framework], 'pathMappings': pathMappings } else: options = {'debugOptions': ['RedirectOutput', framework]} breakpoints = [{ 'source': { 'path': bp_filename }, 'breakpoints': [{ 'line': bp_line }] }] with self.start_debugging(debug_info) as dbg: session = dbg.session with session.wait_for_event('stopped') as result: ( _, req_launch_attach, _, _, _, _, ) = lifecycle_handshake(session, debug_info.starttype, options=options, breakpoints=breakpoints) req_launch_attach.wait() # wait for flask web server start count = 0 path = None while path is None and count < 10: outevent = session.get_awaiter_for_event('output') Awaitable.wait_all(outevent) events = self.find_events(session.received, 'output') count += 1 for e in events: matches = re.findall(re_link, e.body['output']) if len(matches) > 0 and len(matches[0]) > 0 and \ len(matches[0][0].strip()) > 0: path = matches[0][0] break # connect to web server web_result = {} web_client_thread = threading.Thread( target=get_web_string_no_error, args=(path, web_result), name='test.webClient') web_client_thread.start() event = result['msg'] tid = event.body['threadId'] req_stacktrace = session.send_request( 'stackTrace', threadId=tid, ) req_stacktrace.wait() stacktrace = req_stacktrace.resp.body frame_id = stacktrace['stackFrames'][0]['id'] req_scopes = session.send_request( 'scopes', frameId=frame_id, ) req_scopes.wait() scopes = req_scopes.resp.body['scopes'] variables_reference = scopes[0]['variablesReference'] req_variables = session.send_request( 'variables', variablesReference=variables_reference, ) req_variables.wait() variables = req_variables.resp.body['variables'] session.send_request( 'continue', threadId=tid, ) # wait for flask rendering thread to exit web_client_thread.join(timeout=0.1) # shutdown to web server path += 'exit' if path.endswith('/') else '/exit' web_client_thread = threading.Thread( target=get_web_string_no_error, args=(path, None), name='test.webClient.shutdown') web_client_thread.start() web_client_thread.join(timeout=1) received = list(_strip_newline_output_events(session.received)) self.assertGreaterEqual(stacktrace['totalFrames'], 1) self.assert_is_subset( stacktrace, { # We get Python and PTVSD frames as well. # 'totalFrames': 2, 'stackFrames': [{ 'id': 1, 'name': bp_name, 'source': { 'sourceReference': 0, 'path': bp_filename }, 'line': bp_line, 'column': 1, }], }) variables = list(v for v in variables if v['name'] == 'content') self.assert_is_subset(variables, [{ 'name': 'content', 'type': 'str', 'value': repr(bp_var_value), 'presentationHint': { 'attributes': ['rawString'] }, 'evaluateName': 'content' }]) self.assertTrue(web_result['content'].find(bp_var_value) != -1) self.assert_contains(received, [ self.new_event( 'stopped', reason='breakpoint', threadId=tid, text=None, description=None, ), self.new_event('continued', threadId=tid), ]) if framework != 'Django': # TODO: Figure out better way to shutdown Django self.assert_contains(received, [ self.new_event('exited', exitCode=0), self.new_event('terminated'), ])
def test_attach_breakpoints(self): # See https://github.com/Microsoft/ptvsd/issues/448. addr = Address('localhost', 8888) filename = self.write_script('spam.py', """ import sys sys.path.insert(0, {!r}) import ptvsd addr = {} ptvsd.enable_attach(addr) print('== waiting for attach ==') # <waiting> ptvsd.wait_for_attach() # <attached> print('== attached! ==') # <bp 2> print('== done waiting ==') """.format(ROOT, tuple(addr))) lockfile1 = self.workspace.lockfile() done1, _ = set_lock(filename, lockfile1, 'waiting') lockfile2 = self.workspace.lockfile() done2, script = set_lock(filename, lockfile2, 'bp 2') bp1 = find_line(script, 'attached') bp2 = find_line(script, 'bp 2') breakpoints = [{ 'source': {'path': filename}, 'breakpoints': [ {'line': bp1}, {'line': bp2}, ], }] options = { 'pathMappings': [ { 'localRoot': os.path.dirname(filename), 'remoteRoot': os.path.dirname(filename) }, # This specific mapping is for Mac. # For some reason temp paths on Mac get prefixed with # `private` when returned from ptvsd. { 'localRoot': os.path.dirname(filename), 'remoteRoot': '/private' + os.path.dirname(filename) } ] } #DebugAdapter.VERBOSE = True adapter = DebugAdapter.start_embedded(addr, filename) with adapter: with DebugClient() as editor: session = editor.attach_socket(addr, adapter, timeout=5) with session.wait_for_event('thread') as result: with session.wait_for_event('process'): (req_init, req_attach, req_config, reqs_bps, _, req_threads1, ) = lifecycle_handshake(session, 'attach', breakpoints=breakpoints, options=options, threads=True) Awaitable.wait_all(req_init, req_attach, req_config) req_bps, = reqs_bps # There should only be one. event = result['msg'] tid = event.body['threadId'] # Grab the initial output. out1 = next(adapter.output) # "waiting for attach" line = adapter.output.readline() while line: out1 += line line = adapter.output.readline() with session.wait_for_event('stopped'): # Tell the script to proceed (at "# <waiting>"). # This leads to the first breakpoint. done1() req_threads2, req_stacktrace1 = react_to_stopped(session, tid) out2 = str(adapter.output) # "" # Tell the script to proceed (at "# <bp 2>"). This # leads to the second breakpoint. At this point # execution is still stopped at the first breakpoint. done2() with session.wait_for_event('stopped'): with session.wait_for_event('continued'): req_continue1 = session.send_request( 'continue', threadId=tid, ) req_continue1.wait() req_threads3, req_stacktrace2 = react_to_stopped(session, tid) out3 = str(adapter.output) # "attached!" with session.wait_for_event('continued'): req_continue2 = session.send_request( 'continue', threadId=tid, ) req_continue2.wait() adapter.wait() out4 = str(adapter.output) # "done waiting" # Output between enable_attach() and wait_for_attach() may # be sent at a relatively arbitrary time (or not at all). # So we ignore it by removing it from the message list. received = list(_strip_output_event(session.received, u'== waiting for attach ==')) received = list(_strip_newline_output_events(received)) # There's an ordering race with continue/continued that pops # up occasionally. We work around that by manually fixing the # order. for pos, msg in _find_events(received, 'continued'): prev = received[pos-1] if prev.type != 'response' or prev.command != 'continue': received.pop(pos-1) received.insert(pos + 1, prev) # Sometimes the proc ends before the exited and terminated # events are received. received = list(_strip_exit(received)) self.assert_contains(received, [ self.new_version_event(session.received), self.new_response(req_init.req, **INITIALIZE_RESPONSE), self.new_event('initialized'), self.new_response(req_attach.req), self.new_event( 'thread', threadId=tid, reason='started', ), self.new_response(req_threads1.req, **{ 'threads': [{ 'id': 1, 'name': 'MainThread', }], }), self.new_response(req_bps.req, **{ 'breakpoints': [{ 'id': 1, 'line': bp1, 'verified': True, }, { 'id': 2, 'line': bp2, 'verified': True, }], }), self.new_response(req_config.req), self.new_event('process', **{ 'isLocalProcess': True, 'systemProcessId': adapter.pid, 'startMethod': 'attach', 'name': filename, }), self.new_event( 'stopped', threadId=tid, reason='breakpoint', description=None, text=None, ), self.new_response(req_threads2.req, **{ 'threads': [{ 'id': 1, 'name': 'MainThread', }], }), self.new_event( 'module', module={ 'id': 1, 'name': '__main__', 'path': filename, 'package': None, }, reason='new', ), self.new_response(req_stacktrace1.req, **{ 'totalFrames': 1, 'stackFrames': [{ 'id': 1, 'name': '<module>', 'source': { 'path': filename, 'sourceReference': 0, }, 'line': bp1, 'column': 1, }], }), self.new_response(req_continue1.req, **{ 'allThreadsContinued': True }), self.new_event('continued', threadId=tid), self.new_event( 'output', category='stdout', output='== attached! ==', ), self.new_event( 'stopped', threadId=tid, reason='breakpoint', description=None, text=None, ), self.new_response(req_threads3.req, **{ 'threads': [{ 'id': 1, 'name': 'MainThread', }], }), self.new_response(req_stacktrace2.req, **{ 'totalFrames': 1, 'stackFrames': [{ 'id': 2, # TODO: Isn't this the same frame as before? 'name': '<module>', 'source': { 'path': filename, 'sourceReference': 0, }, 'line': bp2, 'column': 1, }], }), self.new_response(req_continue2.req, **{ 'allThreadsContinued': True }), self.new_event('continued', threadId=tid), self.new_event( 'output', category='stdout', output='== done waiting ==', ), #self.new_event( # 'thread', # threadId=tid, # reason='exited', #), #self.new_event('exited', exitCode=0), #self.new_event('terminated'), ]) # before attaching self.assertIn(b'waiting for attach', out1) self.assertNotIn(b'attached!', out1) # after attaching self.assertNotIn('attached!', out2) # after bp1 continue self.assertIn('attached!', out3) self.assertNotIn('done waiting', out3) # after bp2 continue self.assertIn('done waiting', out4)
def test_detach_clear_and_resume(self): addr = Address('localhost', 8888) filename = self.write_script('spam.py', """ import sys sys.path.insert(0, {!r}) import ptvsd addr = {} ptvsd.enable_attach(addr) ptvsd.wait_for_attach() # <before> print('==before==') # <after> print('==after==') # <done> """.format(ROOT, tuple(addr))) lockfile2 = self.workspace.lockfile() done1, _ = set_lock(filename, lockfile2, 'before') lockfile3 = self.workspace.lockfile() _, wait2 = set_release(filename, lockfile3, 'done') lockfile4 = self.workspace.lockfile() done2, script = set_lock(filename, lockfile4, 'done') bp1 = find_line(script, 'before') bp2 = find_line(script, 'after') #DebugAdapter.VERBOSE = True adapter = DebugAdapter.start_embedded(addr, filename) with adapter: with DebugClient() as editor: session1 = editor.attach_socket(addr, adapter, timeout=5) with session1.wait_for_event('thread') as result: with session1.wait_for_event('process'): (req_init1, req_attach1, req_config1, _, _, req_threads1, ) = lifecycle_handshake(session1, 'attach', threads=True) event = result['msg'] tid1 = event.body['threadId'] stopped_event = session1.get_awaiter_for_event('stopped') req_bps = session1.send_request( 'setBreakpoints', source={'path': filename}, breakpoints=[ {'line': bp1}, {'line': bp2}, ], ) req_bps.wait() done1() stopped_event.wait() req_threads2 = session1.send_request('threads') req_stacktrace1 = session1.send_request( 'stackTrace', threadId=tid1, ) out1 = str(adapter.output) # Detach with execution stopped and 1 breakpoint left. req_disconnect = session1.send_request('disconnect') Awaitable.wait_all(req_threads2, req_stacktrace1, req_disconnect) # noqa editor.detach(adapter) try: wait2() except LockTimeoutError: self.fail('execution never resumed upon detach ' 'or breakpoints never cleared') out2 = str(adapter.output) import time time.sleep(2) session2 = editor.attach_socket(addr, adapter, timeout=5) #session2.VERBOSE = True with session2.wait_for_event('thread') as result: with session2.wait_for_event('process'): (req_init2, req_attach2, req_config2, _, _, req_threads3, ) = lifecycle_handshake(session2, 'attach', threads=True) event = result['msg'] tid2 = event.body['threadId'] done2() adapter.wait() out3 = str(adapter.output) received = list(_strip_newline_output_events(session1.received)) self.assert_contains(received, [ self.new_version_event(session1.received), self.new_response(req_init1.req, **INITIALIZE_RESPONSE), self.new_event('initialized'), self.new_response(req_attach1.req), self.new_event( 'thread', threadId=tid1, reason='started', ), self.new_response(req_threads1.req, **{ 'threads': [{ 'id': 1, 'name': 'MainThread', }], }), self.new_response(req_config1.req), self.new_event('process', **{ 'isLocalProcess': True, 'systemProcessId': adapter.pid, 'startMethod': 'attach', 'name': filename, }), self.new_response(req_bps.req, **{ 'breakpoints': [{ 'id': 1, 'line': bp1, 'verified': True, }, { 'id': 2, 'line': bp2, 'verified': True, }], }), self.new_event( 'stopped', threadId=tid1, reason='breakpoint', description=None, text=None, ), self.new_response(req_threads2.req, **{ 'threads': [{ 'id': 1, 'name': 'MainThread', }], }), self.new_response(req_disconnect.req), ]) self.messages.reset_all() received = list(_strip_newline_output_events(session2.received)) # Sometimes the proc ends before the exited and terminated # events are received. received = list(_strip_exit(received)) self.assert_contains(received, [ self.new_version_event(session2.received), self.new_response(req_init2.req, **INITIALIZE_RESPONSE), self.new_event('initialized'), self.new_response(req_attach2.req), self.new_event( 'thread', threadId=tid2, reason='started', ), self.new_response(req_threads3.req, **{ 'threads': [{ 'id': 1, 'name': 'MainThread', }], }), self.new_response(req_config2.req), self.new_event('process', **{ 'isLocalProcess': True, 'systemProcessId': adapter.pid, 'startMethod': 'attach', 'name': filename, }), #self.new_event( # 'thread', # threadId=tid2, # reason='exited', #), #self.new_event('exited', exitCode=0), #self.new_event('terminated'), ]) # at breakpoint self.assertEqual(out1, '') # after detaching self.assertIn('==before==', out2) self.assertIn('==after==', out2) # after reattach self.assertEqual(out3, out2)
def run_test_breaking_into_raised_and_unhandled_exceptions( self, debug_info, expected_source_name): excbreakpoints = [{'filters': ['raised', 'uncaught']}] options = {'debugOptions': ['RedirectOutput']} expected = { 'exceptionId': 'ArithmeticError', 'breakMode': 'always', 'details': { 'typeName': 'ArithmeticError', 'source': expected_source_name } } with self.start_debugging(debug_info) as dbg: stopped = dbg.session.get_awaiter_for_event('stopped') (_, req_launch_attach, _, _, _, _ ) = lifecycle_handshake(dbg.session, debug_info.starttype, excbreakpoints=excbreakpoints, options=options) Awaitable.wait_all(req_launch_attach, stopped) self.assertEqual(stopped.event.body['text'], 'ArithmeticError') self.assertEqual(stopped.event.body['description'], 'Hello') thread_id = stopped.event.body['threadId'] req_exc_info = dbg.session.send_request( 'exceptionInfo', threadId=thread_id) req_exc_info.wait() self.assert_is_subset(req_exc_info.resp.body, expected) stopped2 = dbg.session.get_awaiter_for_event('stopped') continued = dbg.session.get_awaiter_for_event('continued') dbg.session.send_request( 'continue', threadId=thread_id, ).wait() Awaitable.wait_all(stopped2, continued) # Second hit on uncaught exception self.assertEqual(stopped2.event.body['text'], 'ArithmeticError') self.assertEqual(stopped2.event.body['description'], 'Hello') req_exc_info2 = dbg.session.send_request( 'exceptionInfo', threadId=thread_id, ) req_exc_info2.wait() self.assert_is_subset(req_exc_info2.resp.body, expected) continued2 = dbg.session.get_awaiter_for_event('continued') dbg.session.send_request( 'continue', threadId=thread_id, ).wait() Awaitable.wait_all(continued2) received = list(_strip_newline_output_events(dbg.session.received)) self.assert_contains(received, [ self.new_event('continued', threadId=thread_id), self.new_event('continued', threadId=thread_id), # expect 2 events self.new_event('exited', exitCode=0), self.new_event('terminated'), ])
def run_test_variables(self, debug_info): bp_line = 3 breakpoints = [{ 'source': { 'path': debug_info.filename }, 'breakpoints': [{ 'line': bp_line }] }] with self.start_debugging(debug_info) as dbg: session = dbg.session with session.wait_for_event('stopped') as result: ( _, req_launch_attach, _, _, _, _, ) = lifecycle_handshake(session, debug_info.starttype, breakpoints=breakpoints) req_launch_attach.wait() event = result['msg'] tid = event.body['threadId'] req_stacktrace = session.send_request( 'stackTrace', threadId=tid, ) req_stacktrace.wait() frames = req_stacktrace.resp.body['stackFrames'] frame_id = frames[0]['id'] req_scopes = session.send_request( 'scopes', frameId=frame_id, ) req_scopes.wait() scopes = req_scopes.resp.body['scopes'] variables_reference = scopes[0]['variablesReference'] req_variables = session.send_request( 'variables', variablesReference=variables_reference, ) req_variables.wait() variables = req_variables.resp.body['variables'] var_b = list(b for b in variables if b['name'] == 'b') var_b = var_b[0] if len(var_b) == 1 else None if var_b is None: var_b_variables = None else: var_b_ref = var_b['variablesReference'] req_variables = session.send_request( 'variables', variablesReference=var_b_ref, ) req_variables.wait() var_b_variables = req_variables.resp.body['variables'] req_evaluate1 = session.send_request( 'evaluate', expression='a', frameId=frame_id, ) req_evaluate2 = session.send_request( 'evaluate', expression="b['one']", frameId=frame_id, ) Awaitable.wait_all(req_evaluate1, req_evaluate2) var_a_evaluate = req_evaluate1.resp.body var_b_one_evaluate = req_evaluate2.resp.body session.send_request('continue', threadId=tid) # Variables for a, b, __file__, __main__ self.assertGreaterEqual(len(variables), 3) self.assert_is_subset(variables, [{ 'name': 'a', 'type': 'int', 'value': '1', 'evaluateName': 'a' }, { 'name': 'b', 'type': 'dict', 'value': "{'one': 1, 'two': 2}", 'evaluateName': 'b' }, { 'name': '__builtins__', 'type': 'dict', 'evaluateName': '__builtins__' }, { 'name': '__doc__', 'type': 'NoneType', 'value': 'None', 'evaluateName': '__doc__' }, { 'name': '__file__', 'type': 'str', 'presentationHint': { 'attributes': ['rawString'] }, 'evaluateName': '__file__' }, { 'name': '__loader__', 'type': 'SourceFileLoader', 'evaluateName': '__loader__' }, { 'name': '__name__', 'type': 'str', 'value': "'__main__'", 'presentationHint': { 'attributes': ['rawString'] }, 'evaluateName': '__name__' }, { 'name': '__package__', 'type': 'NoneType', 'value': 'None', 'evaluateName': '__package__' }, { 'name': '__spec__', 'type': 'NoneType', 'value': 'None', 'evaluateName': '__spec__' }]) self.assertEqual(var_a_evaluate, { 'type': 'int', 'result': '1', }) assert var_b_variables is not None self.assert_is_subset(var_b_variables, [{ 'type': 'int', 'value': '1', 'evaluateName': "b['one']" }, { 'type': 'int', 'value': '2', 'evaluateName': "b['two']" }, { 'name': '__len__', 'type': 'int', 'value': '2', 'evaluateName': 'b.__len__' }]) self.assertEqual(var_b_one_evaluate, { 'type': 'int', 'result': '1', })