mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
490 lines
18 KiB
Python
490 lines
18 KiB
Python
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
# Licensed under the MIT License. See LICENSE in the project root
|
|
# for license information.
|
|
|
|
from __future__ import print_function, with_statement, absolute_import
|
|
|
|
import sys
|
|
|
|
from tests.helpers import get_marked_line_numbers, print
|
|
from tests.helpers.pattern import ANY
|
|
from tests.helpers.session import DebugSession
|
|
|
|
|
|
def test_variables_and_evaluate(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,
|
|
)
|
|
session.set_breakpoints(bp_file, [bp_line])
|
|
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'] 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 = 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']",
|
|
'variablesReference': 0,
|
|
}
|
|
assert b_variables[1] == {
|
|
'type': 'int',
|
|
'value': '2',
|
|
'name': ANY.such_that(lambda x: x.find('two') > 0),
|
|
'evaluateName': "b['two']",
|
|
'variablesReference': 0,
|
|
}
|
|
assert b_variables[2] == {
|
|
'type': 'int',
|
|
'value': '2',
|
|
'name': '__len__',
|
|
'evaluateName': "len(b)",
|
|
'variablesReference': 0,
|
|
'presentationHint': {'attributes': ['readOnly']},
|
|
}
|
|
|
|
# simple variable
|
|
resp_evaluate1 = 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 = 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 = 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'
|
|
})
|
|
|
|
session.send_request('continue').wait_for_response(freeze=False)
|
|
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,
|
|
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",
|
|
'variablesReference': 0,
|
|
}
|
|
|
|
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_variable_sort(pyfile, run_as, start_method):
|
|
|
|
@pyfile
|
|
def code_to_debug():
|
|
from dbgimporter import import_and_enable_debugger
|
|
import_and_enable_debugger()
|
|
b_test = {"spam": "A", "eggs": "B", "abcd": "C"} # noqa
|
|
_b_test = 12 # noqa
|
|
__b_test = 13 # noqa
|
|
__b_test__ = 14 # noqa
|
|
a_test = 1 # noqa
|
|
_a_test = 2 # noqa
|
|
__a_test = 3 # noqa
|
|
__a_test__ = 4 # noqa
|
|
c_test = {1: "one", 2: "two", 10: "ten"} # noqa
|
|
_c_test = 22 # noqa
|
|
__c_test = 23 # noqa
|
|
__c_test__ = 24 # noqa
|
|
d = 3 # noqa
|
|
print('done')
|
|
|
|
bp_line = 15
|
|
bp_file = code_to_debug
|
|
|
|
with DebugSession() as session:
|
|
session.initialize(
|
|
target=(run_as, bp_file),
|
|
start_method=start_method,
|
|
)
|
|
session.set_breakpoints(bp_file, [bp_line])
|
|
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()
|
|
variable_names = list(
|
|
v['name']
|
|
for v in resp_variables.body['variables']
|
|
if v['name'].find('_test') > 0
|
|
)
|
|
assert variable_names == [
|
|
'a_test', 'b_test', 'c_test', '_a_test', '_b_test', '_c_test',
|
|
'__a_test', '__b_test', '__c_test', '__a_test__', '__b_test__',
|
|
'__c_test__'
|
|
]
|
|
|
|
# ensure string dict keys are sorted
|
|
b_test_variable = list(v for v in resp_variables.body['variables'] if v['name'] == 'b_test')
|
|
assert len(b_test_variable) == 1
|
|
resp_dict_variables = session.send_request('variables', arguments={
|
|
'variablesReference': b_test_variable[0]['variablesReference']
|
|
}).wait_for_response()
|
|
variable_names = list(v['name'][1:5] for v in resp_dict_variables.body['variables'])
|
|
assert len(variable_names) == 4
|
|
assert variable_names[:3] == ['abcd', 'eggs', 'spam']
|
|
|
|
# ensure numeric dict keys are sorted
|
|
c_test_variable = list(v for v in resp_variables.body['variables'] if v['name'] == 'c_test')
|
|
assert len(c_test_variable) == 1
|
|
resp_dict_variables2 = session.send_request('variables', arguments={
|
|
'variablesReference': c_test_variable[0]['variablesReference']
|
|
}).wait_for_response()
|
|
variable_names = list(v['name'] for v in resp_dict_variables2.body['variables'])
|
|
assert len(variable_names) == 4
|
|
# NOTE: this is commented out due to sorting bug #213
|
|
# assert variable_names[:3] == ['1', '2', '10']
|
|
|
|
session.send_request('continue').wait_for_response(freeze=False)
|
|
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() # @bp
|
|
my_func()
|
|
print('done')
|
|
|
|
line_numbers = get_marked_line_numbers(code_to_debug)
|
|
print(line_numbers)
|
|
|
|
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'],
|
|
)
|
|
session.set_breakpoints(code_to_debug, [line_numbers['bp']])
|
|
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_unicode(pyfile, run_as, start_method):
|
|
# On Python 3, variable names can contain Unicode characters.
|
|
# On Python 2, they must be ASCII, but using a Unicode character in an expression should not crash debugger.
|
|
|
|
@pyfile
|
|
def code_to_debug():
|
|
from dbgimporter import import_and_enable_debugger
|
|
import_and_enable_debugger()
|
|
import ptvsd
|
|
# Since Unicode variable name is a SyntaxError at parse time in Python 2,
|
|
# this needs to do a roundabout way of setting it to avoid parse issues.
|
|
globals()[u'\u16A0'] = 123
|
|
ptvsd.break_into_debugger()
|
|
print('break')
|
|
|
|
with DebugSession() as session:
|
|
session.initialize(
|
|
target=(run_as, code_to_debug),
|
|
start_method=start_method,
|
|
)
|
|
session.start_debugging()
|
|
hit = session.wait_for_thread_stopped()
|
|
|
|
resp_eval = session.send_request('evaluate', arguments={
|
|
'expression': '\u16A0', 'frameId': hit.frame_id,
|
|
}).wait_for_response()
|
|
|
|
if sys.version_info >= (3,):
|
|
assert resp_eval.body == ANY.dict_with({
|
|
'type': 'int',
|
|
'result': '123'
|
|
})
|
|
else:
|
|
assert resp_eval.body == ANY.dict_with({
|
|
'type': 'SyntaxError'
|
|
})
|
|
|
|
session.send_request('continue').wait_for_response(freeze=False)
|
|
session.wait_for_exit()
|
|
|
|
|
|
def test_hex_numbers(pyfile, run_as, start_method):
|
|
|
|
@pyfile
|
|
def code_to_debug():
|
|
from dbgimporter import import_and_enable_debugger
|
|
import_and_enable_debugger()
|
|
a = 100
|
|
b = [1, 10, 100]
|
|
c = {10: 10, 100: 100, 1000: 1000}
|
|
d = {(1, 10, 100): (10000, 100000, 100000)}
|
|
print((a, b, c, d)) # @bp
|
|
|
|
line_numbers = get_marked_line_numbers(code_to_debug)
|
|
print(line_numbers)
|
|
|
|
with DebugSession() as session:
|
|
session.initialize(
|
|
target=(run_as, code_to_debug),
|
|
start_method=start_method,
|
|
)
|
|
session.set_breakpoints(code_to_debug, [line_numbers['bp']])
|
|
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'],
|
|
'format': {'hex': True}
|
|
}).wait_for_response()
|
|
variables = list(v for v in resp_variables.body['variables']
|
|
if v['name'] in ('a', 'b', 'c', 'd'))
|
|
a, b, c, d = sorted(variables, key=lambda v: v['name'])
|
|
assert a == ANY.dict_with({
|
|
'name': 'a',
|
|
'value': "0x64",
|
|
'type': 'int',
|
|
'evaluateName': 'a',
|
|
'variablesReference': 0,
|
|
})
|
|
|
|
assert b == ANY.dict_with({
|
|
'name': 'b',
|
|
'value': "[0x1, 0xa, 0x64]",
|
|
'type': 'list',
|
|
'evaluateName': 'b',
|
|
'variablesReference': ANY.dap_id,
|
|
})
|
|
|
|
resp_variables = session.send_request('variables', arguments={
|
|
'variablesReference': b['variablesReference'],
|
|
'format': {'hex': True}
|
|
}).wait_for_response()
|
|
b_children = resp_variables.body['variables']
|
|
assert b_children == [
|
|
{'name': '0x0', 'value': '0x1', 'type': 'int', 'evaluateName': 'b[0]', 'variablesReference': 0, },
|
|
{'name': '0x1', 'value': '0xa', 'type': 'int', 'evaluateName': 'b[1]', 'variablesReference': 0, },
|
|
{'name': '0x2', 'value': '0x64', 'type': 'int', 'evaluateName': 'b[2]', 'variablesReference': 0, },
|
|
{'name': '__len__', 'value': '0x3', 'type': 'int', 'evaluateName': 'len(b)', 'variablesReference': 0, 'presentationHint': {'attributes': ['readOnly']}, },
|
|
]
|
|
|
|
assert c == ANY.dict_with({
|
|
'name': 'c',
|
|
'value': '{0xa: 0xa, 0x64: 0x64, 0x3e8: 0x3e8}',
|
|
'type': 'dict',
|
|
'evaluateName': 'c',
|
|
'variablesReference': ANY.dap_id,
|
|
})
|
|
|
|
resp_variables = session.send_request('variables', arguments={
|
|
'variablesReference': c['variablesReference'],
|
|
'format': {'hex': True}
|
|
}).wait_for_response()
|
|
c_children = resp_variables.body['variables']
|
|
assert c_children == [
|
|
{'name': '0x3e8', 'value': '0x3e8', 'type': 'int', 'evaluateName': 'c[1000]', 'variablesReference': 0, },
|
|
{'name': '0x64', 'value': '0x64', 'type': 'int', 'evaluateName': 'c[100]', 'variablesReference': 0, },
|
|
{'name': '0xa', 'value': '0xa', 'type': 'int', 'evaluateName': 'c[10]', 'variablesReference': 0, },
|
|
{'name': '__len__', 'value': '0x3', 'type': 'int', 'evaluateName': 'len(c)', 'variablesReference': 0, 'presentationHint': {'attributes': ['readOnly']}, }
|
|
]
|
|
|
|
assert d == ANY.dict_with({
|
|
'name': 'd',
|
|
'value': '{(0x1, 0xa, 0x64): (0x2710, 0x186a0, 0x186a0)}',
|
|
'type': 'dict',
|
|
'evaluateName': 'd',
|
|
'variablesReference': ANY.dap_id,
|
|
})
|
|
resp_variables = session.send_request('variables', arguments={
|
|
'variablesReference': d['variablesReference'],
|
|
'format': {'hex': True}
|
|
}).wait_for_response()
|
|
d_children = resp_variables.body['variables']
|
|
assert d_children == [
|
|
{'name': '(0x1, 0xa, 0x64)', 'value': '(0x2710, 0x186a0, 0x186a0)', 'type': 'tuple', 'evaluateName': 'd[(1, 10, 100)]', 'variablesReference': ANY.dap_id},
|
|
{'name': '__len__', 'value': '0x1', 'type': 'int', 'evaluateName': 'len(d)', 'variablesReference': 0, 'presentationHint': {'attributes': ['readOnly']}, }
|
|
]
|
|
|
|
resp_variables = session.send_request('variables', arguments={
|
|
'variablesReference': d_children[0]['variablesReference'],
|
|
'format': {'hex': True}
|
|
}).wait_for_response()
|
|
d_child_of_child = resp_variables.body['variables']
|
|
assert d_child_of_child == [
|
|
{'name': '0x0', 'value': '0x2710', 'type': 'int', 'evaluateName': 'd[(1, 10, 100)][0]', 'variablesReference': 0, },
|
|
{'name': '0x1', 'value': '0x186a0', 'type': 'int', 'evaluateName': 'd[(1, 10, 100)][1]', 'variablesReference': 0, },
|
|
{'name': '0x2', 'value': '0x186a0', 'type': 'int', 'evaluateName': 'd[(1, 10, 100)][2]', 'variablesReference': 0, },
|
|
{'name': '__len__', 'value': '0x3', 'type': 'int', 'evaluateName': 'len(d[(1, 10, 100)])', 'variablesReference': 0, 'presentationHint': {'attributes': ['readOnly']}, }
|
|
]
|
|
|
|
session.send_request('continue').wait_for_response(freeze=False)
|
|
session.wait_for_exit()
|