def test_valid_new_module(self): sink = ModulesEventSink() mgr = ModulesManager(sink) orig_module = sys.modules['ptvsd.untangle'] expected_module = ANY.dict_with({ 'id': ANY.int, 'name': orig_module.__name__, 'package': orig_module.__package__, 'path': orig_module.__file__, 'version': orig_module.__version__, }) assert expected_module == mgr.add_or_get_from_path( ptvsd.untangle.__file__) assert 1 == len(sink.event_data) assert [expected_module] == mgr.get_all() assert sink.event_data == [ { 'event': 'module', 'args': { 'reason': 'new', 'module': expected_module, }, }, ]
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( 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_add_multi_thread(self): sink = ModulesEventSink() self.mgr = ModulesManager(sink) orig_module = sys.modules['ptvsd.untangle'] expected_module = ANY.dict_with({ 'id': ANY.int, 'name': orig_module.__name__, 'package': orig_module.__package__, 'path': orig_module.__file__, 'version': orig_module.__version__, }) self.path = orig_module.__file__ def thread_worker(test, expected): time.sleep(0.01) assert expected_module == test.mgr.add_or_get_from_path(test.path) threads = [] for _ in range(10): thread = threading.Thread(target=thread_worker, args=(self, expected_module)) threads.append(thread) thread.start() for thread in threads: thread.join() assert 1 == len(sink.event_data) assert [expected_module] == self.mgr.get_all()
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_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_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_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_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_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_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 _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_get_only_module(self): sink = ModulesEventSink() mgr = ModulesManager(sink) expected_module = ANY.dict_with({ 'id': 1, 'name': 'abc.xyz', 'package': 'abc', 'path': '/abc/xyz.py', 'version': '1.2.3.4a1', }) mgr.path_to_module_id['/abc/xyz.py'] = 1 mgr.module_id_to_details[1] = expected_module assert expected_module == mgr.add_or_get_from_path('/abc/xyz.py') assert 0 == len(sink.event_data) assert [expected_module] == mgr.get_all()
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_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 test_flask_exception_no_multiproc(ex_type, start_method): ex_line = { 'handled': 21, 'unhandled': 33, }[ex_type] with DebugSession() as session: _initialize_flask_session_no_multiproc(session, start_method) session.send_request('setExceptionBreakpoints', arguments={ 'filters': ['raised', 'uncaught'], }).wait_for_response() session.start_debugging() # wait for Flask web server to start wait_for_connection(FLASK_PORT) base_link = FLASK_LINK link = base_link + ex_type if base_link.endswith('/') else ('/' + ex_type) web_request = get_web_content(link, {}) thread_stopped = session.wait_for_next( Event('stopped', ANY.dict_with({'reason': 'exception'}))) assert thread_stopped == Event( 'stopped', ANY.dict_with({ 'reason': 'exception', 'text': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'description': 'Hello' })) tid = thread_stopped.body['threadId'] resp_exception_info = session.send_request('exceptionInfo', arguments={ 'threadId': tid, }).wait_for_response() exception = resp_exception_info.body assert exception == { 'exceptionId': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'breakMode': 'always', 'description': 'Hello', 'details': { 'message': 'Hello', 'typeName': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'source': ANY.such_that(lambda s: compare_path(s, FLASK1_APP)), 'stackTrace': ANY.such_that(lambda s: True) } } resp_stacktrace = session.send_request('stackTrace', arguments={ 'threadId': tid, }).wait_for_response() assert resp_stacktrace.body['totalFrames'] > 0 frames = resp_stacktrace.body['stackFrames'] assert frames[0] == { 'id': ANY.int, 'name': 'bad_route_' + ex_type, 'source': { 'sourceReference': ANY.int, 'path': ANY.such_that(lambda s: compare_path(s, FLASK1_APP)), }, 'line': ex_line, 'column': 1, } session.send_request('continue').wait_for_response(freeze=False) # ignore response for exception tests web_request.wait_for_response() # shutdown to web server link = base_link + 'exit' if base_link.endswith('/') else '/exit' get_web_content(link).wait_for_response() session.wait_for_exit()
def test_variables_and_evaluate(debug_session, 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 debug_session.initialize(target=(run_as, bp_file), start_method=start_method) debug_session.set_breakpoints(bp_file, [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'] in ['a', 'b', 'c']) assert len(variables) == 3 # variables should be sorted alphabetically assert ['a', 'b', 'c'] == list(v['name'] for v in variables) # get contents of 'b' resp_b_variables = debug_session.send_request( 'variables', arguments={ 'variablesReference': variables[1]['variablesReference'] }).wait_for_response() b_variables = resp_b_variables.body['variables'] assert len(b_variables) == 3 assert b_variables[0] == { 'type': 'int', 'value': '1', 'name': ANY.such_that(lambda x: x.find('one') > 0), 'evaluateName': "b['one']" } assert b_variables[1] == { 'type': 'int', 'value': '2', 'name': ANY.such_that(lambda x: x.find('two') > 0), 'evaluateName': "b['two']" } assert b_variables[2] == { 'type': 'int', 'value': '2', 'name': '__len__', 'evaluateName': "b.__len__" } # simple variable resp_evaluate1 = debug_session.send_request('evaluate', arguments={ 'expression': 'a', 'frameId': hit.frame_id, }).wait_for_response() assert resp_evaluate1.body == ANY.dict_with({'type': 'int', 'result': '1'}) # dict variable resp_evaluate2 = debug_session.send_request('evaluate', arguments={ 'expression': 'b["one"]', 'frameId': hit.frame_id, }).wait_for_response() assert resp_evaluate2.body == ANY.dict_with({'type': 'int', 'result': '1'}) # expression evaluate resp_evaluate3 = debug_session.send_request('evaluate', arguments={ 'expression': 'a + b["one"]', 'frameId': hit.frame_id, }).wait_for_response() assert resp_evaluate3.body == ANY.dict_with({'type': 'int', 'result': '2'}) debug_session.send_request('continue').wait_for_response() debug_session.wait_for_next(Event('continued')) debug_session.wait_for_exit()
def test_return_values(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() class MyClass(object): def do_something(self): return 'did something' def my_func(): return 'did more things' MyClass().do_something() my_func() print('done') expected1 = ANY.dict_with({ 'name': '(return) MyClass.do_something', 'value': "'did something'", 'type': 'str', 'presentationHint': ANY.dict_with({'attributes': ANY.such_that(lambda x: 'readOnly' in x)}), }) expected2 = ANY.dict_with({ 'name': '(return) my_func', 'value': "'did more things'", 'type': 'str', 'presentationHint': ANY.dict_with({'attributes': ANY.such_that(lambda x: 'readOnly' in x)}), }) with DebugSession() as session: session.initialize(target=(run_as, code_to_debug), start_method=start_method, debug_options=['ShowReturnValue'], ignore_unobserved=[Event('continued')]) session.set_breakpoints(code_to_debug, [8]) session.start_debugging() hit = session.wait_for_thread_stopped() session.send_request('next', { 'threadId': hit.thread_id }).wait_for_response() hit = session.wait_for_thread_stopped(reason='step') 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'].startswith('(return)')) assert variables == [expected1] session.send_request('next', { 'threadId': hit.thread_id }).wait_for_response() hit = session.wait_for_thread_stopped(reason='step') # Scope should not have changed so use the same scope 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'].startswith('(return)')) assert variables == [expected1, expected2] session.send_request('continue').wait_for_response() session.wait_for_exit()
def test_variables_and_evaluate(debug_session, simple_hit_paused_on_break): hit = simple_hit_paused_on_break 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'] in ['a', 'b', 'c']) assert len(variables) == 3 # variables should be sorted alphabetically assert ['a', 'b', 'c'] == list(v['name'] for v in variables) # get contents of 'b' resp_b_variables = debug_session.send_request( 'variables', arguments={ 'variablesReference': variables[1]['variablesReference'] }).wait_for_response() b_variables = resp_b_variables.body['variables'] assert len(b_variables) == 3 assert b_variables[0] == { 'type': 'int', 'value': '1', 'name': ANY.such_that(lambda x: x.find('one') > 0), 'evaluateName': "b['one']" } assert b_variables[1] == { 'type': 'int', 'value': '2', 'name': ANY.such_that(lambda x: x.find('two') > 0), 'evaluateName': "b['two']" } assert b_variables[2] == { 'type': 'int', 'value': '2', 'name': '__len__', 'evaluateName': "b.__len__" } # simple variable resp_evaluate1 = debug_session.send_request('evaluate', arguments={ 'expression': 'a', 'frameId': hit.frame_id, }).wait_for_response() assert resp_evaluate1.body == ANY.dict_with({'type': 'int', 'result': '1'}) # dict variable resp_evaluate2 = debug_session.send_request('evaluate', arguments={ 'expression': 'b["one"]', 'frameId': hit.frame_id, }).wait_for_response() assert resp_evaluate2.body == ANY.dict_with({'type': 'int', 'result': '1'}) # expression evaluate resp_evaluate3 = debug_session.send_request('evaluate', arguments={ 'expression': 'a + b["one"]', 'frameId': hit.frame_id, }).wait_for_response() assert resp_evaluate3.body == ANY.dict_with({'type': 'int', 'result': '2'})
def test_multiprocessing(debug_session, pyfile): @pyfile def code_to_debug(): import multiprocessing import platform import sys def child_of_child(q): print('entering child of child') assert q.get() == 2 q.put(3) print('leaving child of child') def child(q): print('entering child') assert q.get() == 1 print('spawning child of child') p = multiprocessing.Process(target=child_of_child, args=(q, )) p.start() p.join() assert q.get() == 3 q.put(4) print('leaving child') if __name__ == '__main__': import backchannel if sys.version_info >= (3, 4): multiprocessing.set_start_method('spawn') else: assert platform.system() == 'Windows' print('spawning child') q = multiprocessing.Queue() p = multiprocessing.Process(target=child, args=(q, )) p.start() print('child spawned') backchannel.write_json(p.pid) q.put(1) assert backchannel.read_json() == 'continue' q.put(2) p.join() assert q.get() == 4 q.close() backchannel.write_json('done') debug_session.ignore_unobserved += [ # The queue module can spawn helper background threads, depending on Python version # and platform. Since this is an implementation detail, we don't care about those. Event('thread', ANY.dict_with({'reason': 'started'})) ] debug_session.multiprocess = True debug_session.prepare_to_run(filename=code_to_debug, 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_pid = debug_session.read_json() child_subprocess = debug_session.wait_for_next(Event('ptvsd_subprocess')) assert child_subprocess == Event( 'ptvsd_subprocess', { 'rootProcessId': root_pid, 'parentProcessId': root_pid, 'processId': child_pid, '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'] 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() debug_session.proceed() child_child_subprocess = debug_session.wait_for_next( Event('ptvsd_subprocess')) assert child_child_subprocess == Event( 'ptvsd_subprocess', { 'rootProcessId': root_pid, 'parentProcessId': child_pid, 'processId': ANY.int, 'port': ANY.int, 'rootStartRequest': { 'seq': ANY.int, 'type': 'request', 'command': root_start_request.command, 'arguments': root_start_request.arguments, } }) child_child_port = child_child_subprocess.body['port'] child_child_session = DebugSession(method='attach_socket', ptvsd_port=child_child_port) child_child_session.ignore_unobserved = debug_session.ignore_unobserved child_child_session.connect() child_child_session.handshake() child_child_session.start_debugging(freeze=False) debug_session.write_json('continue') if sys.version_info >= (3, ): child_child_session.wait_for_termination() child_session.wait_for_termination() else: # These should really be wait_for_termination(), but child processes don't send the # usual sequence of events leading to 'terminate' when they exit for some unclear # reason (ptvsd bug?). So, just wait till they drop connection. child_child_session.wait_for_disconnect() child_session.wait_for_disconnect() assert debug_session.read_json() == 'done'
def test_django_breakpoint_no_multiproc(debug_session, bp_file, bp_line, bp_name, start_method): debug_session.initialize( start_method=start_method, target=('file', DJANGO1_MANAGE), program_args=['runserver', '--noreload', '--nothreading'], debug_options=['Django'], cwd=DJANGO1_ROOT, expected_returncode=ANY.int, # No clean way to kill Flask server ) bp_var_content = 'Django-Django-Test' debug_session.set_breakpoints(bp_file, [bp_line]) debug_session.start_debugging() # wait for Django server to start wait_for_connection(DJANGO_PORT) web_request = get_web_content(DJANGO_LINK, {}) 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'] > 1 frames = resp_stacktrace.body['stackFrames'] assert frames[0] == { 'id': 1, 'name': bp_name, 'source': { 'sourceReference': ANY, 'path': ANY.such_that(lambda s: compare_path(s, bp_file)), }, 'line': bp_line, 'column': 1, } fid = frames[0]['id'] resp_scopes = debug_session.send_request('scopes', arguments={ 'frameId': fid }).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'] == 'content') assert variables == [{ 'name': 'content', 'type': 'str', 'value': repr(bp_var_content), 'presentationHint': { 'attributes': ['rawString'] }, 'evaluateName': 'content' }] debug_session.send_request('continue').wait_for_response() debug_session.wait_for_next(Event('continued')) web_content = web_request.wait_for_response() assert web_content.find(bp_var_content) != -1 # shutdown to web server link = DJANGO_LINK + 'exit' get_web_content(link).wait_for_response()
def test_vsc_exception_options_raise_with_except(pyfile, run_as, start_method, raised, uncaught): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() def raise_with_except(): try: raise ArithmeticError('bad code') except Exception: pass raise_with_except() ex_line = 5 filters = [] filters += ['raised'] if raised == 'raisedOn' else [] filters += ['uncaught'] if uncaught == 'uncaughtOn' else [] with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], ) session.send_request('setExceptionBreakpoints', { 'filters': filters }).wait_for_response() session.start_debugging() expected = ANY.dict_with({ 'exceptionId': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'description': 'bad code', 'breakMode': 'always' if raised == 'raisedOn' else 'unhandled', 'details': ANY.dict_with({ 'typeName': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'message': 'bad code', 'source': ANY.such_that(lambda s: compare_path(code_to_debug, s)), }), }) if raised == 'raisedOn': hit = session.wait_for_thread_stopped(reason='exception') frames = hit.stacktrace.body['stackFrames'] assert ex_line == frames[0]['line'] resp_exc_info = session.send_request('exceptionInfo', { 'threadId': hit.thread_id }).wait_for_response() assert resp_exc_info.body == expected session.send_request('continue').wait_for_response(freeze=False) # uncaught should not 'stop' matter since the exception is caught session.wait_for_exit()
def test_django_breakpoint_multiproc(debug_session, start_method): debug_session.initialize( start_method=start_method, target=('file', DJANGO1_MANAGE), multiprocess=True, program_args=[ 'runserver', ], debug_options=['Django'], cwd=DJANGO1_ROOT, ignore_events=[Event('stopped'), Event('continued')], expected_returncode=ANY.int, # No clean way to kill Flask server ) bp_line = 40 bp_var_content = 'Django-Django-Test' debug_session.set_breakpoints(DJANGO1_MANAGE, [bp_line]) debug_session.start_debugging() child_session = _wait_for_child_process(debug_session) child_session.send_request('setBreakpoints', arguments={ 'source': { 'path': DJANGO1_MANAGE }, 'breakpoints': [ { 'line': bp_line }, ], }).wait_for_response() child_session.start_debugging() # wait for Django server to start while True: child_session.proceed() o = child_session.wait_for_next(Event('output')) if get_url_from_str(o.body['output']) is not None: break web_request = get_web_content(DJANGO_LINK, {}) thread_stopped = child_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 = child_session.send_request('stackTrace', arguments={ 'threadId': tid, }).wait_for_response() assert resp_stacktrace.body['totalFrames'] > 0 frames = resp_stacktrace.body['stackFrames'] assert frames[0] == { 'id': ANY.int, 'name': 'home', 'source': { 'sourceReference': ANY.int, 'path': ANY.such_that(lambda s: compare_path(s, DJANGO1_MANAGE)), }, 'line': bp_line, 'column': 1, } fid = frames[0]['id'] resp_scopes = child_session.send_request('scopes', arguments={ 'frameId': fid }).wait_for_response() scopes = resp_scopes.body['scopes'] assert len(scopes) > 0 resp_variables = child_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'] == 'content') assert variables == [{ 'name': 'content', 'type': 'str', 'value': repr(bp_var_content), 'presentationHint': { 'attributes': ['rawString'] }, 'evaluateName': 'content' }] child_session.send_request('continue').wait_for_response() child_session.wait_for_next(Event('continued')) web_content = web_request.wait_for_response() assert web_content.find(bp_var_content) != -1 # shutdown to web server link = DJANGO_LINK + 'exit' get_web_content(link).wait_for_response()
def test_django_exception_no_multiproc(debug_session, ex_type, ex_line, start_method): debug_session.initialize( start_method=start_method, target=('file', DJANGO1_MANAGE), program_args=['runserver', '--noreload', '--nothreading'], debug_options=['Django'], cwd=DJANGO1_ROOT, expected_returncode=ANY.int, # No clean way to kill Flask server ) debug_session.send_request('setExceptionBreakpoints', arguments={ 'filters': ['raised', 'uncaught'], }).wait_for_response() debug_session.start_debugging() wait_for_connection(DJANGO_PORT) base_link = DJANGO_LINK link = base_link + ex_type if base_link.endswith('/') else ('/' + ex_type) web_request = get_web_content(link, {}) thread_stopped = debug_session.wait_for_next( Event('stopped', ANY.dict_with({'reason': 'exception'}))) assert thread_stopped == Event( 'stopped', ANY.dict_with({ 'reason': 'exception', 'text': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'description': 'Hello' })) tid = thread_stopped.body['threadId'] resp_exception_info = debug_session.send_request('exceptionInfo', arguments={ 'threadId': tid, }).wait_for_response() exception = resp_exception_info.body assert exception == { 'exceptionId': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'breakMode': 'always', 'description': 'Hello', 'details': { 'message': 'Hello', 'typeName': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'source': ANY.such_that(lambda s: compare_path(s, DJANGO1_MANAGE)), 'stackTrace': ANY.such_that(lambda s: True), } } resp_stacktrace = debug_session.send_request('stackTrace', arguments={ 'threadId': tid, }).wait_for_response() assert resp_stacktrace.body['totalFrames'] > 1 frames = resp_stacktrace.body['stackFrames'] assert frames[0] == { 'id': ANY, 'name': 'bad_route_' + ex_type, 'source': { 'sourceReference': ANY, 'path': ANY.such_that(lambda s: compare_path(s, DJANGO1_MANAGE)), }, 'line': ex_line, 'column': 1, } debug_session.send_request('continue').wait_for_response() debug_session.wait_for_next(Event('continued')) # ignore response for exception tests web_request.wait_for_response() # shutdown to web server link = base_link + 'exit' if base_link.endswith('/') else '/exit' get_web_content(link).wait_for_response()
def test_conditional_breakpoint(pyfile, run_as, start_method, condition_key): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() for i in range(0, 10): print(i) expected = { 'condition_var': ('condition', 'i==5', '5', 1), 'hitCondition_#': ('hitCondition', '5', '4', 1), 'hitCondition_eq': ('hitCondition', '==5', '4', 1), 'hitCondition_gt': ('hitCondition', '>5', '5', 5), 'hitCondition_ge': ('hitCondition', '>=5', '4', 6), 'hitCondition_lt': ('hitCondition', '<5', '0', 4), 'hitCondition_le': ('hitCondition', '<=5', '0', 5), 'hitCondition_mod': ('hitCondition', '%3', '2', 3), } condition_type, condition, value, hits = expected[condition_key] bp_line = 4 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_type: condition }], }).wait_for_response() session.start_debugging() hit = session.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] assert bp_line == frames[0]['line'] 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'] == 'i') assert variables == [ ANY.dict_with({ 'name': 'i', 'type': 'int', 'value': value, 'evaluateName': 'i' }) ] session.send_request('continue').wait_for_response(freeze=False) for i in range(1, hits): session.wait_for_thread_stopped() session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_vsc_exception_options_raise_without_except(pyfile, run_as, start_method, raised, uncaught): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() def raise_without_except(): raise ArithmeticError('bad code') raise_without_except() ex_line = 4 filters = [] filters += ['raised'] if raised == 'raisedOn' else [] filters += ['uncaught'] if uncaught == 'uncaughtOn' else [] with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued'), Event('stopped')], expected_returncode=ANY.int, ) session.send_request('setExceptionBreakpoints', { 'filters': filters }).wait_for_response() session.start_debugging() expected = ANY.dict_with({ 'exceptionId': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'description': 'bad code', 'breakMode': 'always' if raised == 'raisedOn' else 'unhandled', 'details': ANY.dict_with({ 'typeName': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'message': 'bad code', 'source': ANY.such_that(lambda s: compare_path(code_to_debug, s)), }), }) if raised == 'raisedOn': hit = session.wait_for_thread_stopped(reason='exception') frames = hit.stacktrace.body['stackFrames'] assert ex_line == frames[0]['line'] resp_exc_info = session.send_request('exceptionInfo', { 'threadId': hit.thread_id }).wait_for_response() assert resp_exc_info.body == expected session.send_request('continue').wait_for_response(freeze=False) # NOTE: debugger stops at each frame if raised and is uncaught # This behavior can be changed by updating 'notify_on_handled_exceptions' # setting we send to pydevd to notify only once. In our test code, we have # two frames, hence two stops. session.wait_for_thread_stopped(reason='exception') session.send_request('continue').wait_for_response(freeze=False) if uncaught == 'uncaughtOn': hit = session.wait_for_thread_stopped(reason='exception') frames = hit.stacktrace.body['stackFrames'] assert ex_line == frames[0]['line'] resp_exc_info = session.send_request('exceptionInfo', { 'threadId': hit.thread_id }).wait_for_response() assert resp_exc_info.body == expected session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_flask_breakpoint_multiproc(start_method): env = { 'FLASK_APP': 'app', 'FLASK_ENV': 'development', 'FLASK_DEBUG': '1', } if platform.system() != 'Windows': locale = 'en_US.utf8' if platform.system( ) == 'Linux' else 'en_US.UTF-8' env.update({ 'LC_ALL': locale, 'LANG': locale, }) with DebugSession() as parent_session: parent_session.initialize( start_method=start_method, target=('module', 'flask'), multiprocess=True, program_args=[ 'run', ], 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 ) bp_line = 11 bp_var_content = 'Flask-Jinja-Test' parent_session.set_breakpoints(FLASK1_APP, [bp_line]) parent_session.start_debugging() with parent_session.connect_to_next_child_session() as child_session: child_session.send_request('setBreakpoints', arguments={ 'source': { 'path': FLASK1_APP }, 'breakpoints': [ { 'line': bp_line }, ], }).wait_for_response() child_session.start_debugging() # wait for Flask server to start wait_for_connection(FLASK_PORT) web_request = get_web_content(FLASK_LINK, {}) thread_stopped = child_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 = child_session.send_request( 'stackTrace', arguments={ 'threadId': tid, }).wait_for_response() assert resp_stacktrace.body['totalFrames'] > 0 frames = resp_stacktrace.body['stackFrames'] assert frames[0] == { 'id': ANY.int, 'name': 'home', 'source': { 'sourceReference': ANY.int, 'path': ANY.such_that(lambda s: compare_path(s, FLASK1_APP)), }, 'line': bp_line, 'column': 1, } fid = frames[0]['id'] resp_scopes = child_session.send_request('scopes', arguments={ 'frameId': fid }).wait_for_response() scopes = resp_scopes.body['scopes'] assert len(scopes) > 0 resp_variables = child_session.send_request( 'variables', arguments={ 'variablesReference': scopes[0]['variablesReference'] }).wait_for_response() variables = [ v for v in resp_variables.body['variables'] if v['name'] == 'content' ] assert variables == [{ 'name': 'content', 'type': 'str', 'value': repr(bp_var_content), 'presentationHint': { 'attributes': ['rawString'] }, 'evaluateName': 'content' }] child_session.send_request('continue').wait_for_response( freeze=False) web_content = web_request.wait_for_response() assert web_content.find(bp_var_content) != -1 # shutdown to web server link = FLASK_LINK + 'exit' get_web_content(link).wait_for_response() child_session.wait_for_termination() parent_session.wait_for_exit()
def test_flask_breakpoint_no_multiproc(bp_target, start_method): bp_file, bp_line, bp_name = { 'code': (FLASK1_APP, 11, 'home'), 'template': (FLASK1_TEMPLATE, 8, 'template') }[bp_target] with DebugSession() as session: _initialize_flask_session_no_multiproc(session, start_method) bp_var_content = 'Flask-Jinja-Test' session.set_breakpoints(bp_file, [bp_line]) session.start_debugging() # wait for Flask web server to start wait_for_connection(FLASK_PORT) link = FLASK_LINK web_request = get_web_content(link, {}) thread_stopped = 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 = session.send_request('stackTrace', arguments={ 'threadId': tid, }).wait_for_response() assert resp_stacktrace.body['totalFrames'] > 0 frames = resp_stacktrace.body['stackFrames'] assert frames[0] == { 'id': ANY.int, 'name': bp_name, 'source': { 'sourceReference': ANY.int, 'path': ANY.such_that(lambda s: compare_path(s, bp_file)), }, 'line': bp_line, 'column': 1, } fid = frames[0]['id'] resp_scopes = session.send_request('scopes', arguments={ 'frameId': fid }).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'] == 'content') assert variables == [{ 'name': 'content', 'type': 'str', 'value': repr(bp_var_content), 'presentationHint': { 'attributes': ['rawString'] }, 'evaluateName': 'content' }] session.send_request('continue').wait_for_response(freeze=False) web_content = web_request.wait_for_response() assert web_content.find(bp_var_content) != -1 # shutdown to web server link = FLASK_LINK + 'exit' get_web_content(link).wait_for_response() session.wait_for_exit()
def test_condition_with_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}', 'condition': 'i==5' }], }).wait_for_response() session.start_debugging() hit = session.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] assert bp_line == frames[0]['line'] 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'] == 'i') assert variables == [ ANY.dict_with({ 'name': 'i', 'type': 'int', 'value': '5', 'evaluateName': 'i' }) ] session.send_request('continue').wait_for_response(freeze=False) 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)) assert 15 in logged assert 5 in values