Test fixes.

This commit is contained in:
Pavel Minaev 2019-09-11 14:37:27 -07:00 committed by Pavel Minaev
parent d2055ed968
commit eb1bb4dd5b
13 changed files with 100 additions and 145 deletions

View file

@ -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):

View file

@ -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.

View file

@ -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)

View file

@ -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()

View file

@ -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
)

View file

@ -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",

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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"

View file

@ -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"],

View file

@ -1,3 +1,2 @@
print('one')
print('two')
print('three')
from debug_me import backchannel
backchannel.send("ok")

View file

@ -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)