def test_exit_normally_with_wait_on_abnormal_exit_enabled( debug_session, pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() print('one') print('two') print('three') debug_session.debug_options += ['WaitOnAbnormalExit'] bp_line = 5 bp_file = code_to_debug debug_session.initialize(target=(run_as, bp_file), start_method=start_method) debug_session.set_breakpoints(bp_file, [bp_line]) debug_session.start_debugging() debug_session.wait_for_next(Event('stopped'), ANY.dict_with({'reason': 'breakpoint'})) debug_session.send_request('continue').wait_for_response() debug_session.wait_for_next(Event('continued')) debug_session.proceed() debug_session.wait_for_next(Event('exited')) output = [ e.body['output'] for e in debug_session.all_occurrences_of(Event('output')) if len(e.body['output']) >= 3 and e.body['category'] == 'stdout' ] assert len(output) == 3 assert output == ['one', 'two', 'three'] debug_session.wait_for_exit()
def test_continue_on_disconnect_for_attach(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() import backchannel backchannel.write_json('continued') bp_line = 4 with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[ Event('continued'), Event('exited'), Event('terminated') ], use_backchannel=True, ) session.set_breakpoints(code_to_debug, [bp_line]) session.start_debugging() hit = session.wait_for_thread_stopped('breakpoint') frames = hit.stacktrace.body['stackFrames'] assert frames[0]['line'] == bp_line session.send_request('disconnect').wait_for_response() session.wait_for_disconnect() assert 'continued' == session.read_json()
def _initialize_flask_session_no_multiproc(session, start_method): env = { 'FLASK_APP': 'app.py', 'FLASK_ENV': 'development', 'FLASK_DEBUG': '0', } if platform.system() != 'Windows': locale = 'en_US.utf8' if platform.system( ) == 'Linux' else 'en_US.UTF-8' env.update({ 'LC_ALL': locale, 'LANG': locale, }) session.initialize( start_method=start_method, target=('module', 'flask'), program_args=['run', '--no-debugger', '--no-reload', '--with-threads'], ignore_unobserved=[Event('stopped'), Event('continued')], debug_options=['Jinja'], cwd=FLASK1_ROOT, env=env, expected_returncode=ANY.int, # No clean way to kill Flask server )
def test_subprocess(debug_session, pyfile): @pyfile def child(): import sys import backchannel backchannel.write_json(sys.argv) @pyfile def parent(): import os import subprocess import sys argv = [sys.executable] argv += [sys.argv[1], '--arg1', '--arg2', '--arg3'] env = os.environ.copy() process = subprocess.Popen(argv, env=env, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) process.wait() debug_session.multiprocess = True debug_session.program_args += [child] debug_session.prepare_to_run(filename=parent, backchannel=True) debug_session.start_debugging() root_start_request, = debug_session.all_occurrences_of( Request('launch') | Request('attach')) root_process, = debug_session.all_occurrences_of(Event('process')) root_pid = int(root_process.body['systemProcessId']) child_subprocess = debug_session.wait_for_next(Event('ptvsd_subprocess')) assert child_subprocess == Event( 'ptvsd_subprocess', { 'rootProcessId': root_pid, 'parentProcessId': root_pid, 'processId': ANY.int, 'port': ANY.int, 'rootStartRequest': { 'seq': ANY.int, 'type': 'request', 'command': root_start_request.command, 'arguments': root_start_request.arguments, } }) child_port = child_subprocess.body['port'] debug_session.proceed() child_session = DebugSession(method='attach_socket', ptvsd_port=child_port) child_session.ignore_unobserved = debug_session.ignore_unobserved child_session.connect() child_session.handshake() child_session.start_debugging() child_argv = debug_session.read_json() assert child_argv == [child, '--arg1', '--arg2', '--arg3'] debug_session.wait_for_exit()
def test_completions_scope(debug_session, pyfile, bp_line, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() class SomeClass(): def __init__(self, someVar): self.some_var = someVar def do_someting(self): someVariable = self.some_var return someVariable def someFunction(someVar): someVariable = someVar return SomeClass(someVariable).do_someting() someFunction('value') print('done') expected = expected_at_line[bp_line] debug_session.ignore_unobserved += [Event('stopped'), Event('continued')] debug_session.initialize(target=(run_as, code_to_debug), start_method=start_method) debug_session.set_breakpoints(code_to_debug, [bp_line]) debug_session.start_debugging() thread_stopped = debug_session.wait_for_next( Event('stopped'), ANY.dict_with({'reason': 'breakpoint'})) assert thread_stopped.body['threadId'] is not None tid = thread_stopped.body['threadId'] resp_stacktrace = debug_session.send_request('stackTrace', arguments={ 'threadId': tid, }).wait_for_response() assert resp_stacktrace.body['totalFrames'] > 0 frames = resp_stacktrace.body['stackFrames'] assert len(frames) > 0 fid = frames[0]['id'] resp_completions = debug_session.send_request('completions', arguments={ 'text': 'some', 'frameId': fid, 'column': 1 }).wait_for_response() targets = resp_completions.body['targets'] debug_session.send_request('continue').wait_for_response() debug_session.wait_for_next(Event('continued')) targets.sort(key=lambda t: t['label']) expected.sort(key=lambda t: t['label']) assert targets == expected debug_session.wait_for_exit()
def test_set_variable(debug_session, pyfile, run_as): @pyfile def code_to_debug(): a = 1 print(a) bp_line = 2 bp_file = code_to_debug debug_session.common_setup(bp_file, run_as, breakpoints=[bp_line]) debug_session.start_debugging() hit = debug_session.wait_for_thread_stopped() resp_scopes = debug_session.send_request('scopes', arguments={ 'frameId': hit.frame_id }).wait_for_response() scopes = resp_scopes.body['scopes'] assert len(scopes) > 0 resp_variables = debug_session.send_request( 'variables', arguments={ 'variablesReference': scopes[0]['variablesReference'] }).wait_for_response() variables = list(v for v in resp_variables.body['variables'] if v['name'] == 'a') assert len(variables) == 1 assert variables[0] == { 'type': 'int', 'value': '1', 'name': 'a', 'evaluateName': "a" } resp_set_variable = debug_session.send_request( 'setVariable', arguments={ 'variablesReference': scopes[0]['variablesReference'], 'name': 'a', 'value': '1000' }).wait_for_response() assert resp_set_variable.body == ANY.dict_with({ 'type': 'int', 'value': '1000' }) debug_session.send_request('continue').wait_for_response() debug_session.wait_for_next(Event('continued')) debug_session.wait_for_next(Event('output')) output = [ e for e in debug_session.all_occurrences_of(Event('output')) if e.body['output'].startswith('1000') ] assert any(output) debug_session.wait_for_exit()
def test_subprocess(debug_session, pyfile, start_method, run_as): @pyfile def child(): import sys import backchannel from dbgimporter import import_and_enable_debugger import_and_enable_debugger() backchannel.write_json(sys.argv) @pyfile def parent(): import os import subprocess import sys from dbgimporter import import_and_enable_debugger import_and_enable_debugger() argv = [sys.executable, sys.argv[1], '--arg1', '--arg2', '--arg3'] env = os.environ.copy() process = subprocess.Popen(argv, env=env, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) process.wait() debug_session.program_args += [child] debug_session.initialize(multiprocess=True, target=(run_as, parent), start_method=start_method, use_backchannel=True) debug_session.start_debugging() root_start_request, = debug_session.all_occurrences_of(Request('launch') | Request('attach')) root_process, = debug_session.all_occurrences_of(Event('process')) root_pid = int(root_process.body['systemProcessId']) child_subprocess = debug_session.wait_for_next(Event('ptvsd_subprocess')) assert child_subprocess == Event('ptvsd_subprocess', { 'rootProcessId': root_pid, 'parentProcessId': root_pid, 'processId': ANY.int, 'port': ANY.int, 'rootStartRequest': { 'seq': ANY.int, 'type': 'request', 'command': root_start_request.command, 'arguments': root_start_request.arguments, } }) child_pid = child_subprocess.body['processId'] child_port = child_subprocess.body['port'] debug_session.proceed() child_session = DebugSession(start_method=START_METHOD_CMDLINE, ptvsd_port=child_port, pid=child_pid) child_session.ignore_unobserved = debug_session.ignore_unobserved child_session.connect() child_session.handshake() child_session.start_debugging() child_argv = debug_session.read_json() assert child_argv == [child, '--arg1', '--arg2', '--arg3'] child_session.wait_for_exit() debug_session.wait_for_exit()
def test_wait_on_abnormal_exit_enabled(debug_session, pyfile, run_as): @pyfile def code_to_debug(): import sys print('one') print('two') print('three') sys.exit(12345) debug_session.debug_options += ['WaitOnAbnormalExit'] bp_line = 5 bp_file = code_to_debug _common_setup(debug_session, bp_file, run_as) debug_session.send_request('setBreakpoints', arguments={ 'source': { 'path': bp_file }, 'breakpoints': [ { 'line': bp_line }, ], }).wait_for_response() debug_session.start_debugging() debug_session.wait_for_next(Event('stopped'), ANY.dict_with({'reason': 'breakpoint'})) debug_session.send_request('continue').wait_for_response() debug_session.wait_for_next(Event('continued')) debug_session.proceed() debug_session.expected_returncode = ANY.int debug_session.wait_for_next(Event('exited')) output = [ e.body['output'] for e in debug_session.all_occurrences_of(Event('output')) if len(e.body['output']) >= 3 and e.body['category'] == 'stdout' ] assert len(output) == 3 assert output == ['one', 'two', 'three'] debug_session.process.stdin.write(b' \r\n') debug_session.wait_for_exit() def _decode(text): if isinstance(text, bytes): return text.decode('utf-8') return text assert any(l for l in debug_session.output_data['OUT'] if _decode(l).startswith('Press'))
def test_wait_on_abnormal_exit_enabled(debug_session, pyfile, run_as, start_method): @pyfile def code_to_debug(): import sys from dbgimporter import import_and_enable_debugger import_and_enable_debugger() print('one') print('two') print('three') sys.exit(12345) debug_session.debug_options += ['WaitOnAbnormalExit'] bp_line = 7 bp_file = code_to_debug debug_session.initialize(target=(run_as, bp_file), start_method=start_method) debug_session.set_breakpoints(bp_file, [bp_line]) debug_session.start_debugging() debug_session.wait_for_next(Event('stopped'), ANY.dict_with({'reason': 'breakpoint'})) debug_session.send_request('continue').wait_for_response() debug_session.wait_for_next(Event('continued')) debug_session.proceed() debug_session.expected_returncode = ANY.int debug_session.wait_for_next(Event('exited')) output = [ e.body['output'] for e in debug_session.all_occurrences_of(Event('output')) if len(e.body['output']) >= 3 and e.body['category'] == 'stdout' ] assert len(output) == 3 assert output == ['one', 'two', 'three'] debug_session.process.stdin.write(b' \r\n') debug_session.wait_for_exit() def _decode(text): if isinstance(text, bytes): return text.decode('utf-8') return text assert any(l for l in debug_session.output_data['OUT'] if _decode(l).startswith('Press'))
def test_exit_on_disconnect_for_launch(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() import os.path fp = os.join(os.path.dirname(os.path.abspath(__file__)), 'here.txt') # should not execute this with open(fp, 'w') as f: print('Should not continue after disconnect on launch', file=f) bp_line = 4 with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], use_backchannel=True, expected_returncode=ANY.int, ) session.set_breakpoints(code_to_debug, [bp_line]) session.start_debugging() hit = session.wait_for_thread_stopped('breakpoint') frames = hit.stacktrace.body['stackFrames'] assert frames[0]['line'] == bp_line session.send_request('disconnect').wait_for_response() session.wait_for_exit() fp = os.join(os.path.dirname(os.path.abspath(code_to_debug)), 'here.txt') assert not os.path.exists(fp)
def simple_hit_paused_on_break(debug_session, pyfile, run_as): ''' Starts debug session with a pre-defined code sample, yields with a breakpoint hit and when finished, resumes the execution and waits for the debug session to exit. :note: fixture will run with all permutations of the debug_session parameters as well as the run_as parameters. ''' from pytests.helpers.timeline import Event @pyfile def code_to_debug(): a = 1 b = {"one": 1, "two": 2} c = 3 print([a, b, c]) bp_line = 4 bp_file = code_to_debug debug_session.common_setup(bp_file, run_as, breakpoints=[bp_line]) debug_session.start_debugging() hit = debug_session.wait_for_thread_stopped() yield hit debug_session.send_request('continue').wait_for_response() debug_session.wait_for_next(Event('continued')) debug_session.wait_for_exit()
def test_breakpoint_function(pyfile, run_as, start_method): @pyfile def code_to_debug(): # NOTE: These tests verify break_into_debugger for launch # and attach cases. For attach this is always after wait_for_attach from dbgimporter import import_and_enable_debugger import_and_enable_debugger() breakpoint() print('break here') with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')] ) session.start_debugging() hit = session.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] path = frames[0]['source']['path'] assert path.endswith('code_to_debug.py') or path.endswith('<string>') assert frames[0]['line'] == 6 session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_break_on_entry(pyfile, run_as, start_method): @pyfile def code_to_debug(): import backchannel from dbgimporter import import_and_enable_debugger import_and_enable_debugger() backchannel.write_json('done') with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, debug_options=['StopOnEntry'], ignore_unobserved=[Event('continued')], use_backchannel=True, ) session.start_debugging() thread_stopped, resp_stacktrace, tid, _ = session.wait_for_thread_stopped() frames = resp_stacktrace.body['stackFrames'] assert frames[0]['line'] == 1 session.send_request('continue').wait_for_response(freeze=False) session.wait_for_termination() assert session.read_json() == 'done' session.wait_for_exit()
def test_set_variable(pyfile, run_as, start_method): @pyfile def code_to_debug(): import backchannel from dbgimporter import import_and_enable_debugger import_and_enable_debugger() import ptvsd a = 1 ptvsd.break_into_debugger() backchannel.write_json(a) with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], use_backchannel=True, ) session.start_debugging() hit = session.wait_for_thread_stopped() resp_scopes = session.send_request('scopes', arguments={ 'frameId': hit.frame_id }).wait_for_response() scopes = resp_scopes.body['scopes'] assert len(scopes) > 0 resp_variables = session.send_request( 'variables', arguments={ 'variablesReference': scopes[0]['variablesReference'] }).wait_for_response() variables = list(v for v in resp_variables.body['variables'] if v['name'] == 'a') assert len(variables) == 1 assert variables[0] == { 'type': 'int', 'value': '1', 'name': 'a', 'evaluateName': "a" } resp_set_variable = session.send_request( 'setVariable', arguments={ 'variablesReference': scopes[0]['variablesReference'], 'name': 'a', 'value': '1000' }).wait_for_response() assert resp_set_variable.body == ANY.dict_with({ 'type': 'int', 'value': '1000' }) session.send_request('continue').wait_for_response(freeze=False) assert session.read_json() == 1000 session.wait_for_exit()
def test_exit_normally_with_wait_on_abnormal_exit_enabled(pyfile, run_as, start_method): @pyfile def code_to_debug(): import backchannel from dbgimporter import import_and_enable_debugger import_and_enable_debugger() import ptvsd ptvsd.break_into_debugger() backchannel.write_json('done') with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, debug_options=['WaitOnAbnormalExit'], ignore_unobserved=[Event('continued')], use_backchannel=True, ) session.start_debugging() session.wait_for_thread_stopped() session.send_request('continue').wait_for_response(freeze=False) session.wait_for_termination() assert session.read_json() == 'done' session.wait_for_exit()
def _common_setup(debug_session, path, run_as): debug_session.ignore_unobserved += [ Event('thread', ANY.dict_with({'reason': 'started'})), Event('module') ] if run_as == 'file': debug_session.prepare_to_run(filename=path) elif run_as == 'module': debug_session.add_file_to_pythonpath(path) debug_session.prepare_to_run(module='code_to_debug') elif run_as == 'code': with open(path, 'r') as f: code = f.read() debug_session.prepare_to_run(code=code) else: pytest.fail()
def test_run(debug_session, pyfile, run_as): @pyfile def code_to_debug(): import os import sys import backchannel print('begin') assert backchannel.read_json() == 'continue' backchannel.write_json(os.path.abspath(sys.modules['ptvsd'].__file__)) print('end') if run_as == 'file': debug_session.prepare_to_run(filename=code_to_debug, backchannel=True) elif run_as == 'module': debug_session.add_file_to_pythonpath(code_to_debug) debug_session.prepare_to_run(module='code_to_debug', backchannel=True) elif run_as == 'code': with open(code_to_debug, 'r') as f: code = f.read() debug_session.prepare_to_run(code=code, backchannel=True) else: pytest.fail() debug_session.start_debugging() assert debug_session.timeline.is_frozen process_event, = debug_session.all_occurrences_of(Event('process')) assert process_event == Event( 'process', ANY.dict_with({ 'name': ANY if run_as == 'code' else ANY.such_that(lambda name: ( # There can be a difference in file extension (.py/.pyc/.pyo) on clean runs. name == code_to_debug or name == code_to_debug + 'c' or name == code_to_debug + 'o')), })) debug_session.write_json('continue') ptvsd_path = debug_session.read_json() expected_ptvsd_path = os.path.abspath(ptvsd.__file__) assert (ptvsd_path == expected_ptvsd_path or ptvsd_path == expected_ptvsd_path + 'c' or ptvsd_path == expected_ptvsd_path + 'o') debug_session.wait_for_exit()
def test_module_events(pyfile, run_as, start_method): @pyfile def module2(): # import_and_enable_debugger() def do_more_things(): print('done') @pyfile def module1(): # import_and_enable_debugger() import module2 def do_something(): module2.do_more_things() @pyfile def test_code(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() from module1 import do_something do_something() bp_line = 3 with DebugSession() as session: session.initialize( target=(run_as, test_code), start_method=start_method, ignore_unobserved=[Event('stopped'), Event('continued')], ) session.set_breakpoints(module2, [bp_line]) session.start_debugging() session.wait_for_thread_stopped() modules = session.all_occurrences_of(Event('module')) modules = [(m.body['module']['name'], m.body['module']['path']) for m in modules] assert modules[:3] == [ ('module2', Path(module2)), ('module1', Path(module1)), ('__main__', Path(test_code)), ] session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_log_point(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() a = 10 for i in range(1, a): print('value: %d' % i) bp_line = 5 with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], ) session.send_request('setBreakpoints', arguments={ 'source': { 'path': code_to_debug }, 'breakpoints': [{ 'line': bp_line, 'logMessage': 'log: {a + i}' }], }).wait_for_response() session.start_debugging() session.wait_for_exit() assert session.get_stderr_as_string() == b'' output = session.all_occurrences_of( Event('output', ANY.dict_with({'category': 'stdout'}))) output_str = ''.join(o.body['output'] for o in output) logged = sorted( int(i) for i in re.findall(r"log:\s([0-9]*)", output_str)) values = sorted( int(i) for i in re.findall(r"value:\s([0-9]*)", output_str)) # NOTE: Due to https://github.com/Microsoft/ptvsd/issues/1028 we may not get # all output events. Once that is fixed we should check for the exact output # and log value assert len(logged) > 0 assert len(values) > 0
def test_exit_normally_with_wait_on_abnormal_exit_enabled( debug_session, pyfile, run_as): @pyfile def code_to_debug(): print('one') print('two') print('three') debug_session.debug_options += ['WaitOnAbnormalExit'] bp_line = 3 bp_file = code_to_debug _common_setup(debug_session, bp_file, run_as) debug_session.send_request('setBreakpoints', arguments={ 'source': { 'path': bp_file }, 'breakpoints': [ { 'line': bp_line }, ], }).wait_for_response() debug_session.start_debugging() debug_session.wait_for_next(Event('stopped'), ANY.dict_with({'reason': 'breakpoint'})) debug_session.send_request('continue').wait_for_response() debug_session.wait_for_next(Event('continued')) debug_session.proceed() debug_session.wait_for_next(Event('exited')) output = [ e.body['output'] for e in debug_session.all_occurrences_of(Event('output')) if len(e.body['output']) >= 3 and e.body['category'] == 'stdout' ] assert len(output) == 3 assert output == ['one', 'two', 'three'] debug_session.wait_for_exit()
def test_stack_format(pyfile, run_as, start_method, module, line): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() from test_module import do_something do_something() @pyfile def test_module(): # import_and_enable_debugger() def do_something(): print('break here') bp_line = 3 with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('stopped'), Event('continued')], ) session.set_breakpoints(test_module, [bp_line]) session.start_debugging() hit = session.wait_for_thread_stopped() resp_stacktrace = session.send_request('stackTrace', arguments={ 'threadId': hit.thread_id, 'format': { 'module': module, 'line': line }, }).wait_for_response() assert resp_stacktrace.body['totalFrames'] > 0 frames = resp_stacktrace.body['stackFrames'] assert line == (frames[0]['name'].find(': ' + str(bp_line)) > -1) assert module == (frames[0]['name'].find('test_module') > -1) session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_break_on_entry(debug_session, pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() print('one') print('two') print('three') debug_session.debug_options += ['StopOnEntry'] debug_session.initialize(target=(run_as, code_to_debug), start_method=start_method) debug_session.start_debugging() thread_stopped = debug_session.wait_for_next( Event('stopped'), ANY.dict_with({'reason': 'step'})) assert thread_stopped.body['threadId'] is not None tid = thread_stopped.body['threadId'] resp_stacktrace = debug_session.send_request('stackTrace', arguments={ 'threadId': tid, }).wait_for_response() assert resp_stacktrace.body['totalFrames'] > 0 frames = resp_stacktrace.body['stackFrames'] assert frames[0]['line'] == 1 debug_session.send_request('continue').wait_for_response() debug_session.wait_for_next(Event('continued')) debug_session.wait_for_next(Event('exited')) output = [ e.body['output'] for e in debug_session.all_occurrences_of(Event('output')) if len(e.body['output']) >= 3 and e.body['category'] == 'stdout' ] assert len(output) == 3 assert output == ['one', 'two', 'three'] debug_session.wait_for_exit()
def _wait_for_child_process(debug_session): child_subprocess = debug_session.wait_for_next(Event('ptvsd_subprocess')) assert child_subprocess.body['port'] != 0 child_port = child_subprocess.body['port'] child_session = DebugSession(start_method=START_METHOD_CMDLINE, ptvsd_port=child_port) child_session.ignore_unobserved = debug_session.ignore_unobserved child_session.debug_options = debug_session.debug_options child_session.connect() child_session.handshake() return child_session
def test_run(pyfile, run_as, start_method): @pyfile def code_to_debug(): import os import sys import backchannel from dbgimporter import import_and_enable_debugger import_and_enable_debugger() print('begin') assert backchannel.read_json() == 'continue' backchannel.write_json(os.path.abspath(sys.modules['ptvsd'].__file__)) print('end') with DebugSession() as session: session.initialize(target=(run_as, code_to_debug), start_method=start_method, use_backchannel=True) session.start_debugging() assert session.timeline.is_frozen process_event, = session.all_occurrences_of(Event('process')) assert process_event == Event( 'process', ANY.dict_with({ 'name': ANY if run_as == 'code' else ANY.such_that(lambda name: ( # There can be a difference in file extension (.py/.pyc/.pyo) on clean runs. name == code_to_debug or name == code_to_debug + 'c' or name == code_to_debug + 'o')), })) session.write_json('continue') ptvsd_path = session.read_json() expected_ptvsd_path = os.path.abspath(ptvsd.__file__) assert (ptvsd_path == expected_ptvsd_path or ptvsd_path == expected_ptvsd_path + 'c' or ptvsd_path == expected_ptvsd_path + 'o') session.wait_for_exit()
def test_completions_cases(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() a = 1 b = {"one": 1, "two": 2} c = 3 print([a, b, c]) bp_line = 6 bp_file = code_to_debug with DebugSession() as session: session.initialize( target=(run_as, bp_file), start_method=start_method, ignore_unobserved=[Event('continued')], ) session.set_breakpoints(bp_file, [bp_line]) session.start_debugging() hit = session.wait_for_thread_stopped() response = session.send_request('completions', arguments={ 'frameId': hit.frame_id, 'text': 'b.', 'column': 3, }).wait_for_response() labels = set(target['label'] for target in response.body['targets']) assert labels.issuperset(['get', 'items', 'keys', 'setdefault', 'update', 'values']) response = session.send_request('completions', arguments={ 'frameId': hit.frame_id, 'text': 'x = b.setdefault', 'column': 13, }).wait_for_response() assert response.body['targets'] == [ {'label': 'setdefault', 'length': 6, 'start': 6, 'type': 'function'}] response = session.send_request('completions', arguments={ 'frameId': hit.frame_id, 'text': 'not_there', 'column': 10, }).wait_for_response() assert not response.body['targets'] session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_autokill(debug_session, pyfile, start_method, run_as): @pyfile def child(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() while True: pass @pyfile def parent(): import backchannel import os import subprocess import sys from dbgimporter import import_and_enable_debugger import_and_enable_debugger() argv = [sys.executable, sys.argv[1]] env = os.environ.copy() subprocess.Popen(argv, env=env, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) backchannel.read_json() debug_session.program_args += [child] debug_session.initialize(multiprocess=True, target=(run_as, parent), start_method=start_method, use_backchannel=True) debug_session.start_debugging() child_subprocess = debug_session.wait_for_next(Event('ptvsd_subprocess')) child_pid = child_subprocess.body['processId'] child_port = child_subprocess.body['port'] debug_session.proceed() child_session = DebugSession(start_method=START_METHOD_CMDLINE, ptvsd_port=child_port, pid=child_pid) child_session.expected_returncode = ANY child_session.connect() child_session.handshake() child_session.start_debugging() if debug_session.start_method == START_METHOD_LAUNCH: # In launch scenario, terminate the parent process by disconnecting from it. debug_session.expected_returncode = ANY disconnect = debug_session.send_request('disconnect', {}) debug_session.wait_for_next(Response(disconnect)) else: # In attach scenario, just let the parent process run to completion. debug_session.expected_returncode = 0 debug_session.write_json(None) debug_session.wait_for_exit() child_session.wait_for_exit()
def test_with_tab_in_output(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() print('Hello\tWorld') with DebugSession() as session: session.initialize(target=(run_as, code_to_debug), start_method=start_method) session.start_debugging() session.wait_for_exit() output = session.all_occurrences_of( Event('output', ANY.dict_with({'category': 'stdout'}))) output_str = ''.join(o.body['output'] for o in output) assert output_str.startswith('Hello\tWorld')
def test_error_in_condition(pyfile, run_as, start_method, error_name): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() def do_something_bad(): raise ArithmeticError() for i in range(1, 10): pass # NOTE: NameError in condition, is a special case. Pydevd is configured to skip # traceback for name errors. See https://github.com/Microsoft/ptvsd/issues/853 # for more details. For all other errors we should be printing traceback. condition = { 'NameError': ('x==5'), # 'x' does not exist in the debuggee 'OtherError': ('do_something_bad()==5') # throws some error } bp_line = 5 with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], ) session.send_request('setBreakpoints', arguments={ 'source': { 'path': code_to_debug }, 'breakpoints': [{ 'line': bp_line, 'condition': condition[error_name], }], }).wait_for_response() session.start_debugging() session.wait_for_exit() assert session.get_stdout_as_string() == b'' if error_name == 'NameError': assert session.get_stderr_as_string() == b'' else: assert session.get_stderr_as_string().find(b'ArithmeticError') > 0
def test_attach(run_as, wait_for_attach, is_attached, break_into): testfile = os.path.join(get_test_root('attach'), 'attach1.py') with DebugSession() as session: env = { 'PTVSD_TEST_HOST': 'localhost', 'PTVSD_TEST_PORT': str(session.ptvsd_port), } if wait_for_attach == 'waitOn': env['PTVSD_WAIT_FOR_ATTACH'] = '1' if is_attached == 'attachCheckOn': env['PTVSD_IS_ATTACHED'] = '1' if break_into == 'break': env['PTVSD_BREAK_INTO_DBG'] = '1' session.initialize( target=(run_as, testfile), start_method='launch', ignore_unobserved=[Event('continued')], env=env, use_backchannel=True, ) session.start_debugging() if wait_for_attach == 'waitOn': assert session.read_json() == 'wait_for_attach' if is_attached == 'attachCheckOn': assert session.read_json() == 'is_attached' if break_into == 'break': assert session.read_json() == 'break_into_debugger' hit = session.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] assert 32 == frames[0]['line'] else: # pause test session.write_json('pause_test') session.send_request('pause').wait_for_response(freeze=False) hit = session.wait_for_thread_stopped(reason='pause') frames = hit.stacktrace.body['stackFrames'] # Note: no longer asserting line as it can even stop on different files # (such as as backchannel.py). # assert frames[0]['line'] in [27, 28, 29] session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_path_with_ampersand(run_as, start_method): bp_line = 4 testfile = os.path.join(BP_TEST_ROOT, 'a&b', 'test.py') with DebugSession() as session: session.initialize( target=(run_as, testfile), start_method=start_method, ignore_unobserved=[Event('continued')], ) session.set_breakpoints(testfile, [bp_line]) session.start_debugging() hit = session.wait_for_thread_stopped('breakpoint') frames = hit.stacktrace.body['stackFrames'] assert compare_path(frames[0]['source']['path'], testfile, show=False) session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()