mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Test fixes.
This commit is contained in:
parent
d2055ed968
commit
eb1bb4dd5b
13 changed files with 100 additions and 145 deletions
|
|
@ -260,7 +260,14 @@ class IDE(components.Component):
|
|||
@message_handler
|
||||
def continue_request(self, request):
|
||||
request.arguments["threadId"] = "*"
|
||||
return self.server.channel.delegate(request)
|
||||
|
||||
try:
|
||||
return self.server.channel.delegate(request)
|
||||
except messaging.NoMoreMessages:
|
||||
# pydevd can sometimes allow the debuggee to exit before the queued
|
||||
# "continue" response gets sent. Thus, a failed "continue" response
|
||||
# indicating that the server disconnected should be treated as success.
|
||||
return {"allThreadsContinued": True}
|
||||
|
||||
@message_handler
|
||||
def ptvsd_systemInfo_request(self, request):
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class Session(util.Observable):
|
|||
"""Debug options as specified by "launch" or "attach" request."""
|
||||
|
||||
self.is_finalizing = False
|
||||
"""Whether the session is inside finalize()."""
|
||||
"""Whether finalize() has been invoked."""
|
||||
|
||||
self.observers += [lambda *_: self.notify_changed()]
|
||||
|
||||
|
|
@ -275,31 +275,18 @@ class Session(util.Observable):
|
|||
)
|
||||
|
||||
try:
|
||||
proc = subprocess.Popen(
|
||||
subprocess.Popen(
|
||||
cmdline,
|
||||
bufsize=0,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
# This process will immediately exit after injecting debug server
|
||||
proc.wait()
|
||||
except Exception as exc:
|
||||
log.exception("{0} failed to inject debugger", self)
|
||||
raise messaging.MessageHandlingError(
|
||||
fmt("Failed to inject debugger: {0}", exc)
|
||||
)
|
||||
if proc.returncode != 0:
|
||||
log.exception(
|
||||
"{0} failed to inject debugger with error code {1}",
|
||||
self,
|
||||
proc.returncode,
|
||||
)
|
||||
raise messaging.MessageHandlingError(
|
||||
fmt(
|
||||
"Failed to inject debugger with error code {0}", proc.returncode
|
||||
)
|
||||
)
|
||||
|
||||
def finalize(self, why, terminate_debuggee=False):
|
||||
"""Finalizes the debug session.
|
||||
|
|
|
|||
|
|
@ -984,20 +984,21 @@ class Response(Message):
|
|||
raise self.body
|
||||
|
||||
@staticmethod
|
||||
def _parse(channel, message_dict):
|
||||
seq = message_dict("seq", int)
|
||||
def _parse(channel, message_dict, body=None):
|
||||
seq = message_dict("seq", int) if (body is None) else None
|
||||
request_seq = message_dict("request_seq", int)
|
||||
command = message_dict("command", unicode)
|
||||
success = message_dict("success", bool)
|
||||
if success:
|
||||
body = message_dict("body", _payload)
|
||||
else:
|
||||
error_message = message_dict("message", unicode)
|
||||
exc_type = MessageHandlingError
|
||||
if error_message.startswith(InvalidMessageError.PREFIX):
|
||||
error_message = error_message[len(InvalidMessageError.PREFIX) :]
|
||||
exc_type = InvalidMessageError
|
||||
body = exc_type(error_message, silent=True)
|
||||
if body is None:
|
||||
if success:
|
||||
body = message_dict("body", _payload)
|
||||
else:
|
||||
error_message = message_dict("message", unicode)
|
||||
exc_type = MessageHandlingError
|
||||
if error_message.startswith(InvalidMessageError.PREFIX):
|
||||
error_message = error_message[len(InvalidMessageError.PREFIX) :]
|
||||
exc_type = InvalidMessageError
|
||||
body = exc_type(error_message, silent=True)
|
||||
|
||||
try:
|
||||
with channel:
|
||||
|
|
@ -1386,14 +1387,7 @@ class JsonMessageChannel(object):
|
|||
"message": err_message,
|
||||
},
|
||||
)
|
||||
|
||||
response = Response._parse(self, response_json)
|
||||
response.seq = None
|
||||
response.body = exc
|
||||
|
||||
response_json.message = request.response = response
|
||||
request._enqueue_response_handlers()
|
||||
|
||||
Response._parse(self, response_json, body=exc)
|
||||
assert not len(self._sent_requests)
|
||||
|
||||
self._enqueue_handlers(Disconnect(self), self._handle_disconnect)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ class Session(object):
|
|||
timeline.Event("thread", some.dict.containing({"reason": "exited"})),
|
||||
timeline.Event("output", some.dict.containing({"category": "stdout"})),
|
||||
timeline.Event("output", some.dict.containing({"category": "stderr"})),
|
||||
timeline.Event("output", some.dict.containing({"category": "console"})),
|
||||
]
|
||||
|
||||
def __init__(
|
||||
|
|
@ -444,6 +445,8 @@ class Session(object):
|
|||
|
||||
try:
|
||||
self.request("disconnect")
|
||||
except messaging.JsonIOError:
|
||||
pass
|
||||
finally:
|
||||
try:
|
||||
self.channel.close()
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ class lines:
|
|||
|
||||
|
||||
def _initialize_session(session, multiprocess=False, exit_code=0):
|
||||
if multiprocess:
|
||||
pytest.skip("https://github.com/microsoft/ptvsd/issues/1706")
|
||||
|
||||
args = ["runserver"]
|
||||
if not multiprocess:
|
||||
args += ["--noreload"]
|
||||
|
|
@ -36,7 +39,7 @@ def _initialize_session(session, multiprocess=False, exit_code=0):
|
|||
session.configure(
|
||||
"program", paths.app_py,
|
||||
cwd=paths.django1,
|
||||
multiprocess=multiprocess,
|
||||
subProcess=multiprocess,
|
||||
args=args,
|
||||
django=True
|
||||
)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ class lines:
|
|||
|
||||
|
||||
def _initialize_session(session, multiprocess=False, exit_code=0):
|
||||
if multiprocess:
|
||||
pytest.skip("https://github.com/microsoft/ptvsd/issues/1706")
|
||||
|
||||
env = {
|
||||
"FLASK_APP": paths.app_py,
|
||||
"FLASK_ENV": "development",
|
||||
|
|
|
|||
|
|
@ -20,35 +20,28 @@ def test_justmycode_frames(pyfile, start_method, run_as, jmc):
|
|||
|
||||
with debug.Session(start_method) as session:
|
||||
session.configure(run_as, code_to_debug, justMyCode=bool(jmc))
|
||||
session.set_breakpoints(code_to_debug, all)
|
||||
|
||||
bp_line = code_to_debug.lines["bp"]
|
||||
actual_bps = session.set_breakpoints(code_to_debug, [bp_line])
|
||||
actual_bps = [bp["line"] for bp in actual_bps]
|
||||
session.start_debugging()
|
||||
|
||||
hit = session.wait_for_stop()
|
||||
assert hit.frames[0] == some.dict.containing(
|
||||
{
|
||||
"line": bp_line,
|
||||
"source": some.dict.containing({"path": some.path(code_to_debug)}),
|
||||
}
|
||||
stop = session.wait_for_stop(
|
||||
"breakpoint",
|
||||
expected_frames=[
|
||||
some.dap.frame(code_to_debug, "bp")
|
||||
],
|
||||
)
|
||||
|
||||
if jmc:
|
||||
assert len(hit.frames) == 1
|
||||
session.send_request(
|
||||
"stepIn", {"threadId": hit.thread_id}
|
||||
).wait_for_response()
|
||||
# 'step' should terminate the debuggee
|
||||
assert len(stop.frames) == 1
|
||||
else:
|
||||
assert len(hit.frames) >= 1
|
||||
session.send_request(
|
||||
"stepIn", {"threadId": hit.thread_id}
|
||||
).wait_for_response()
|
||||
assert len(stop.frames) >= 1
|
||||
|
||||
# 'step' should enter stdlib
|
||||
hit2 = session.wait_for_stop()
|
||||
assert hit2.frames[0]["source"]["path"] != some.path(code_to_debug)
|
||||
session.request("stepIn", {"threadId": stop.thread_id})
|
||||
|
||||
# 'continue' should terminate the debuggee
|
||||
if not jmc:
|
||||
# "stepIn" should stop somewhere inside stdlib
|
||||
session.wait_for_stop(
|
||||
"step",
|
||||
expected_frames=[
|
||||
some.dap.frame(~some.str.equal_to(code_to_debug), some.int)
|
||||
],
|
||||
)
|
||||
session.request_continue()
|
||||
|
|
|
|||
|
|
@ -32,11 +32,12 @@ def test_log_cli(pyfile, tmpdir, start_method, run_as, cli):
|
|||
|
||||
with debug.Session(start_method) as session:
|
||||
with check_logs(tmpdir, session):
|
||||
env = {}
|
||||
if cli == "arg":
|
||||
session.log_dir = str(tmpdir)
|
||||
else:
|
||||
session.env["PTVSD_LOG_DIR"] = str(tmpdir)
|
||||
session.configure(run_as, code_to_debug)
|
||||
env["PTVSD_LOG_DIR"] = str(tmpdir)
|
||||
session.configure(run_as, code_to_debug, env=env)
|
||||
session.start_debugging()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -67,9 +67,7 @@ def test_redirect_output(pyfile, start_method, run_as, redirect):
|
|||
() # @wait_for_output
|
||||
|
||||
with debug.Session(start_method) as session:
|
||||
if redirect == "disabled":
|
||||
session.debug_options -= {"RedirectOutput"} # enabled by default
|
||||
session.configure(run_as, code_to_debug)
|
||||
session.configure(run_as, code_to_debug, redirectOutput=(redirect == "enabled"))
|
||||
session.set_breakpoints(code_to_debug, all)
|
||||
session.start_debugging()
|
||||
|
||||
|
|
|
|||
|
|
@ -45,15 +45,10 @@ def test_run(pyfile, start_method, run_as):
|
|||
|
||||
|
||||
def test_run_submodule():
|
||||
with debug.Session("launch") as session:
|
||||
with debug.Session(start_methods.Launch, backchannel=True) as session:
|
||||
session.configure("module", "pkg1.sub", cwd=test_data / "testpkgs")
|
||||
session.start_debugging()
|
||||
session.wait_for_next(
|
||||
Event(
|
||||
"output",
|
||||
some.dict.containing({"category": "stdout", "output": "three"}),
|
||||
)
|
||||
)
|
||||
session.backchannel.expect("ok")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("run_as", ["program", "module", "code"])
|
||||
|
|
@ -71,7 +66,7 @@ def test_nodebug(pyfile, run_as):
|
|||
run_as, code_to_debug, noDebug=True, console="internalConsole"
|
||||
)
|
||||
|
||||
with pytest.raises(messaging.InvalidMessageError):
|
||||
with pytest.raises(messaging.MessageHandlingError):
|
||||
session.set_breakpoints(code_to_debug, all)
|
||||
|
||||
session.start_debugging()
|
||||
|
|
@ -80,38 +75,5 @@ def test_nodebug(pyfile, run_as):
|
|||
# Breakpoint shouldn't be hit.
|
||||
|
||||
session.expect_realized(
|
||||
Event(
|
||||
"output", some.dict.containing({"category": "stdout", "output": "ok"})
|
||||
)
|
||||
Event("output", some.dict.containing({"category": "stdout", "output": "ok"}))
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("run_as", ["script", "module"])
|
||||
def test_run_vs(pyfile, run_as):
|
||||
@pyfile
|
||||
def code_to_debug():
|
||||
from debug_me import backchannel
|
||||
|
||||
print("ok")
|
||||
backchannel.send("ok")
|
||||
|
||||
@pyfile
|
||||
def ptvsd_launcher():
|
||||
from debug_me import backchannel
|
||||
import ptvsd.debugger
|
||||
|
||||
args = tuple(backchannel.receive())
|
||||
ptvsd.debugger.debug(*args)
|
||||
|
||||
filename = "code_to_debug" if run_as == "module" else code_to_debug
|
||||
with debug.Session("custom_client", backchannel=True) as session:
|
||||
backchannel = session.backchannel
|
||||
|
||||
@session.before_connect
|
||||
def before_connect():
|
||||
backchannel.send([filename, session.ptvsd_port, None, None, run_as])
|
||||
|
||||
session.configure("program", ptvsd_launcher)
|
||||
session.start_debugging()
|
||||
|
||||
assert backchannel.receive() == "ok"
|
||||
|
|
|
|||
|
|
@ -39,10 +39,7 @@ def test_thread_count(pyfile, start_method, run_as, count):
|
|||
stop = True
|
||||
|
||||
with debug.Session(start_method) as session:
|
||||
session.configure(
|
||||
run_as, code_to_debug,
|
||||
args=[str(count)],
|
||||
)
|
||||
session.configure(run_as, code_to_debug, args=[str(count)])
|
||||
session.set_breakpoints(code_to_debug, [code_to_debug.lines["bp"]])
|
||||
session.start_debugging()
|
||||
session.wait_for_stop()
|
||||
|
|
@ -53,16 +50,15 @@ def test_thread_count(pyfile, start_method, run_as, count):
|
|||
session.request_continue()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('stepping_resumes_all_threads', [None, True, False])
|
||||
def test_step_multi_threads(pyfile, run_as, start_method, stepping_resumes_all_threads):
|
||||
|
||||
@pytest.mark.parametrize("resume", ["default", "resume_all", "resume_one"])
|
||||
def test_step_multi_threads(pyfile, run_as, start_method, resume):
|
||||
@pyfile
|
||||
def code_to_debug():
|
||||
'''
|
||||
"""
|
||||
After breaking on the thread 1, thread 2 should pause waiting for the event1 to be set,
|
||||
so, when we step return on thread 1, the program should finish if all threads are resumed
|
||||
or should keep waiting for the thread 2 to run if only thread 1 is resumed.
|
||||
'''
|
||||
"""
|
||||
import threading
|
||||
import debug_me # noqa
|
||||
|
||||
|
|
@ -73,12 +69,12 @@ def test_step_multi_threads(pyfile, run_as, start_method, stepping_resumes_all_t
|
|||
|
||||
def _thread1():
|
||||
while not event0.is_set():
|
||||
event0.wait(timeout=.001)
|
||||
event0.wait(timeout=0.001)
|
||||
|
||||
event1.set() # @break_thread_1
|
||||
|
||||
while not event2.is_set():
|
||||
event2.wait(timeout=.001)
|
||||
event2.wait(timeout=0.001)
|
||||
# Note: we can only get here if thread 2 is also released.
|
||||
|
||||
event3.set()
|
||||
|
|
@ -87,16 +83,16 @@ def test_step_multi_threads(pyfile, run_as, start_method, stepping_resumes_all_t
|
|||
event0.set()
|
||||
|
||||
while not event1.is_set():
|
||||
event1.wait(timeout=.001)
|
||||
event1.wait(timeout=0.001)
|
||||
|
||||
event2.set()
|
||||
|
||||
while not event3.is_set():
|
||||
event3.wait(timeout=.001)
|
||||
event3.wait(timeout=0.001)
|
||||
|
||||
threads = [
|
||||
threading.Thread(target=_thread1, name='thread1'),
|
||||
threading.Thread(target=_thread2, name='thread2'),
|
||||
threading.Thread(target=_thread1, name="thread1"),
|
||||
threading.Thread(target=_thread2, name="thread2"),
|
||||
]
|
||||
for t in threads:
|
||||
t.start()
|
||||
|
|
@ -105,37 +101,43 @@ def test_step_multi_threads(pyfile, run_as, start_method, stepping_resumes_all_t
|
|||
t.join()
|
||||
|
||||
with debug.Session(start_method) as session:
|
||||
session.configure(run_as, code_to_debug, steppingResumesAllThreads=stepping_resumes_all_threads)
|
||||
session.set_breakpoints(code_to_debug, [code_to_debug.lines['break_thread_1']])
|
||||
debug_config = {}
|
||||
if resume == "resume_all":
|
||||
debug_config["steppingResumesAllThreads"] = True
|
||||
elif resume == "resume_one":
|
||||
debug_config["steppingResumesAllThreads"] = False
|
||||
session.configure(run_as, code_to_debug, **debug_config)
|
||||
|
||||
session.set_breakpoints(code_to_debug, all)
|
||||
session.start_debugging()
|
||||
|
||||
stop_info = session.wait_for_stop()
|
||||
resp_threads = session.send_request('threads').wait_for_response()
|
||||
assert len(resp_threads.body['threads']) == 3
|
||||
thread_name_to_id = dict((t['name'], t['id']) for t in resp_threads.body['threads'])
|
||||
assert stop_info.thread_id == thread_name_to_id['thread1']
|
||||
threads = session.request("threads")
|
||||
assert len(threads["threads"]) == 3
|
||||
|
||||
if stepping_resumes_all_threads or stepping_resumes_all_threads is None:
|
||||
# stepping_resumes_all_threads == None means we should use default (which is to
|
||||
# resume all threads) -- in which case stepping out will exit the program.
|
||||
session.send_request('stepOut', {'threadId': stop_info.thread_id}).wait_for_response(freeze=False)
|
||||
thread_name_to_id = {t["name"]: t["id"] for t in threads["threads"]}
|
||||
assert stop_info.thread_id == thread_name_to_id["thread1"]
|
||||
|
||||
else:
|
||||
session.send_request('stepOut', {'threadId': stop_info.thread_id}).wait_for_response()
|
||||
if resume == "resume_one":
|
||||
session.request("stepOut", {"threadId": stop_info.thread_id})
|
||||
# Wait a second and check that threads are still there.
|
||||
time.sleep(1)
|
||||
|
||||
resp_stacktrace = session.send_request('stackTrace', arguments={
|
||||
'threadId': thread_name_to_id['thread1'],
|
||||
}).wait_for_response()
|
||||
assert '_thread1' in [x['name'] for x in resp_stacktrace.body['stackFrames']]
|
||||
stack_trace = session.request(
|
||||
"stackTrace", {"threadId": thread_name_to_id["thread1"]}
|
||||
)
|
||||
assert "_thread1" in [frame["name"] for frame in stack_trace["stackFrames"]]
|
||||
|
||||
resp_stacktrace = session.send_request('stackTrace', arguments={
|
||||
'threadId': thread_name_to_id['thread2'],
|
||||
}).wait_for_response()
|
||||
assert '_thread2' in [x['name'] for x in resp_stacktrace.body['stackFrames']]
|
||||
stack_trace = session.request(
|
||||
"stackTrace", {"threadId": thread_name_to_id["thread2"]}
|
||||
)
|
||||
assert "_thread2" in [frame["name"] for frame in stack_trace["stackFrames"]]
|
||||
|
||||
session.request_continue()
|
||||
|
||||
else:
|
||||
session.request("stepOut", {"threadId": stop_info.thread_id}, freeze=False)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
platform.system() not in ["Windows", "Linux", "Darwin"],
|
||||
|
|
|
|||
|
|
@ -1,3 +1,2 @@
|
|||
print('one')
|
||||
print('two')
|
||||
print('three')
|
||||
from debug_me import backchannel
|
||||
backchannel.send("ok")
|
||||
|
|
|
|||
|
|
@ -1003,6 +1003,9 @@ class MessageOccurrence(Occurrence):
|
|||
"""
|
||||
return [("seq", self.message.seq), ("type", self.TYPE)]
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return self.message(*args, **kwargs)
|
||||
|
||||
def describe_circumstances(self):
|
||||
id = collections.OrderedDict(self._id)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue