mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
parent
2b0140e22c
commit
14982d90ba
41 changed files with 317 additions and 148 deletions
|
|
@ -45,7 +45,8 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio
|
|||
"Jinja", // Enables Jinja (Flask) Template debugging
|
||||
"FixFilePathCase", // See FIX_FILE_PATH_CASE in wrapper.py
|
||||
"DebugStdLib" // Whether to enable debugging of standard library functions
|
||||
"WindowsClient" // Whether client OS is Windows or not
|
||||
"WindowsClient" // Whether client OS is Windows
|
||||
"UnixClient" // Whether client OS is Unix
|
||||
],
|
||||
"pathMappings": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -713,7 +713,7 @@ DEBUG_OPTIONS_PARSER = {
|
|||
'DJANGO_DEBUG': bool_parser,
|
||||
'FLASK_DEBUG': bool_parser,
|
||||
'FIX_FILE_PATH_CASE': bool_parser,
|
||||
'WINDOWS_CLIENT': bool_parser,
|
||||
'CLIENT_OS_TYPE': unquote,
|
||||
'DEBUG_STDLIB': bool_parser,
|
||||
}
|
||||
|
||||
|
|
@ -727,7 +727,8 @@ DEBUG_OPTIONS_BY_FLAG = {
|
|||
'Jinja': 'FLASK_DEBUG=True',
|
||||
'FixFilePathCase': 'FIX_FILE_PATH_CASE=True',
|
||||
'DebugStdLib': 'DEBUG_STDLIB=True',
|
||||
'WindowsClient': 'WINDOWS_CLIENT=True',
|
||||
'WindowsClient': 'CLIENT_OS_TYPE=WINDOWS',
|
||||
'UnixClient': 'CLIENT_OS_TYPE=UNIX',
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -776,7 +777,7 @@ def _parse_debug_options(opts):
|
|||
INTERPRETER_OPTIONS=string
|
||||
WEB_BROWSER_URL=string url
|
||||
DJANGO_DEBUG=True|False
|
||||
WINDOWS_CLIENT=True|False
|
||||
CLIENT_OS_TYPE=WINDOWS|UNIX
|
||||
DEBUG_STDLIB=True|False
|
||||
"""
|
||||
options = {}
|
||||
|
|
@ -793,8 +794,8 @@ def _parse_debug_options(opts):
|
|||
except KeyError:
|
||||
continue
|
||||
|
||||
if 'WINDOWS_CLIENT' not in options:
|
||||
options['WINDOWS_CLIENT'] = platform.system() == 'Windows' # noqa
|
||||
if 'CLIENT_OS_TYPE' not in options:
|
||||
options['CLIENT_OS_TYPE'] = 'WINDOWS' if platform.system() == 'Windows' else 'UNIX' # noqa
|
||||
|
||||
return options
|
||||
|
||||
|
|
@ -1224,6 +1225,7 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
|
|||
self.source_map = IDMap()
|
||||
self.enable_source_references = False
|
||||
self.next_var_ref = 0
|
||||
self._path_mappings = []
|
||||
self.exceptions_mgr = ExceptionsManager(self)
|
||||
self.modules_mgr = ModulesManager(self)
|
||||
self.internals_filter = InternalsFilter()
|
||||
|
|
@ -1419,34 +1421,34 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
|
|||
return True
|
||||
|
||||
def _initialize_path_maps(self, args):
|
||||
pathMaps = []
|
||||
self._path_mappings = []
|
||||
for pathMapping in args.get('pathMappings', []):
|
||||
localRoot = pathMapping.get('localRoot', '')
|
||||
remoteRoot = pathMapping.get('remoteRoot', '')
|
||||
if (len(localRoot) > 0 and len(remoteRoot) > 0):
|
||||
pathMaps.append((localRoot, remoteRoot))
|
||||
self._path_mappings.append((localRoot, remoteRoot))
|
||||
|
||||
if len(pathMaps) > 0:
|
||||
pydevd_file_utils.setup_client_server_paths(pathMaps)
|
||||
if len(self._path_mappings) > 0:
|
||||
pydevd_file_utils.setup_client_server_paths(self._path_mappings)
|
||||
|
||||
def _send_cmd_version_command(self):
|
||||
cmd = pydevd_comm.CMD_VERSION
|
||||
windows_client = self.debug_options.get(
|
||||
'WINDOWS_CLIENT',
|
||||
platform.system() == 'Windows')
|
||||
os_id = 'WINDOWS' if windows_client else 'UNIX'
|
||||
default_os_type = 'WINDOWS' if platform.system() == 'Windows' else 'UNIX' # noqa
|
||||
client_os_type = self.debug_options.get(
|
||||
'CLIENT_OS_TYPE', default_os_type)
|
||||
os_id = client_os_type
|
||||
msg = '1.1\t{}\tID'.format(os_id)
|
||||
return self.pydevd_request(cmd, msg)
|
||||
|
||||
@async_handler
|
||||
def _handle_attach(self, args):
|
||||
self._initialize_path_maps(args)
|
||||
yield self._send_cmd_version_command()
|
||||
self._initialize_path_maps(args)
|
||||
|
||||
@async_handler
|
||||
def _handle_launch(self, args):
|
||||
self._initialize_path_maps(args)
|
||||
yield self._send_cmd_version_command()
|
||||
self._initialize_path_maps(args)
|
||||
|
||||
def _handle_detach(self):
|
||||
debug('detaching')
|
||||
|
|
@ -1557,34 +1559,20 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
|
|||
if self.start_reason == 'launch':
|
||||
return 0
|
||||
|
||||
# If we have no path mappings, then always enable source references.
|
||||
autogen = len(self._path_mappings) == 0
|
||||
|
||||
try:
|
||||
return self.source_map.to_vscode(filename, autogen=False)
|
||||
return self.source_map.to_vscode(filename, autogen=autogen)
|
||||
except KeyError:
|
||||
# If attaching to a local process (then remote and local are same)
|
||||
for local_prefix, remote_prefix in pydevd_file_utils.PATHS_FROM_ECLIPSE_TO_PYTHON: # noqa
|
||||
if local_prefix != remote_prefix:
|
||||
continue
|
||||
if filename.startswith(local_prefix): # noqa
|
||||
return 0
|
||||
if platform.system() == 'Windows' and filename.upper().startswith(local_prefix.upper()): # noqa
|
||||
return 0
|
||||
pass
|
||||
|
||||
client_filename = pydevd_file_utils.norm_file_to_client(filename)
|
||||
# If file has been mapped, then source is available on client.
|
||||
for local_prefix, remote_prefix in self._path_mappings:
|
||||
if filename.startswith(local_prefix):
|
||||
return 0
|
||||
|
||||
# If the mapped file is the same as the file we provided,
|
||||
# then we can generate a soure reference.
|
||||
if client_filename == filename:
|
||||
return 0
|
||||
elif platform.system() == 'Windows' and \
|
||||
client_filename.upper() == filename.upper():
|
||||
return 0
|
||||
elif client_filename.replace('\\', '/') == filename.replace('\\', '/'):
|
||||
# If remote is Unix and local is Windows, then PyDevD will
|
||||
# replace the path separator in remote with with
|
||||
# the os path separator of remote client
|
||||
return 0
|
||||
else:
|
||||
return self.source_map.to_vscode(filename, autogen=True)
|
||||
return self.source_map.to_vscode(filename, autogen=True)
|
||||
|
||||
@async_handler
|
||||
def on_stackTrace(self, request, args):
|
||||
|
|
|
|||
|
|
@ -53,6 +53,13 @@ COPIED_ENV = [
|
|||
]
|
||||
|
||||
|
||||
try:
|
||||
ConnectionRefusedError
|
||||
except Exception:
|
||||
class ConnectionRefusedError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def _copy_env(verbose=False, env=None):
|
||||
variables = {k: v for k, v in os.environ.items() if k in COPIED_ENV}
|
||||
# TODO: Be smarter about the seed?
|
||||
|
|
|
|||
|
|
@ -125,12 +125,20 @@ class LifecycleTests(HighlevelTest, unittest.TestCase):
|
|||
# TODO: Ensure we see the "terminated" and "exited" events.
|
||||
raise NotImplementedError
|
||||
|
||||
def test_attach_from_unix_os(self):
|
||||
attach_args = {'options': 'WINDOWS_CLIENT=False'}
|
||||
def test_attach_from_unix_os_vsc(self):
|
||||
attach_args = {'debugOptions': ['UnixClient']}
|
||||
self.attach(expected_os_id='UNIX', attach_args=attach_args)
|
||||
|
||||
def test_attach_from_unix_os(self):
|
||||
attach_args = {'options': 'CLIENT_OS_TYPE=UNIX'}
|
||||
self.attach(expected_os_id='UNIX', attach_args=attach_args)
|
||||
|
||||
def test_attach_from_win_os_vsc(self):
|
||||
attach_args = {'debugOptions': ['WindowsClient']}
|
||||
self.attach(expected_os_id='WINDOWS', attach_args=attach_args)
|
||||
|
||||
def test_attach_from_windows_os(self):
|
||||
attach_args = {'options': 'WINDOWS_CLIENT=True'}
|
||||
attach_args = {'options': 'CLIENT_OS_TYPE=WINDOWS'}
|
||||
self.attach(expected_os_id='WINDOWS', attach_args=attach_args)
|
||||
|
||||
def test_launch(self):
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
import mymod_bar.bar
|
||||
|
||||
|
||||
def do_foo():
|
||||
mymod_bar.bar.do_bar()
|
||||
|
||||
|
||||
do_foo()
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import mypkg_bar.bar
|
||||
|
||||
|
||||
def do_foo():
|
||||
mypkg_bar.bar.do_bar()
|
||||
|
||||
|
||||
do_foo()
|
||||
11
tests/resources/system_tests/test_forever/attach_forever.py
Normal file
11
tests/resources/system_tests/test_forever/attach_forever.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import ptvsd
|
||||
import sys
|
||||
import time
|
||||
|
||||
ptvsd.enable_attach((sys.argv[1], sys.argv[2]))
|
||||
|
||||
i = 0
|
||||
while True:
|
||||
time.sleep(0.1)
|
||||
print(i)
|
||||
i += 1
|
||||
|
|
@ -2,7 +2,9 @@ import contextlib
|
|||
import os
|
||||
import ptvsd
|
||||
import signal
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
import unittest
|
||||
|
||||
from collections import namedtuple
|
||||
|
|
@ -234,24 +236,46 @@ class LifecycleTestsBase(TestsBase, unittest.TestCase):
|
|||
pass
|
||||
time.sleep(1) # wait for socket connections to die out.
|
||||
|
||||
def _wrap_and_reraise(ex, session):
|
||||
def _wrap_and_reraise(session, ex, exc_type, exc_value, exc_traceback):
|
||||
"""If we have connetion errors, then re-raised wrapped in
|
||||
ConnectionTimeoutError. If using py3, then chain exceptions so
|
||||
we do not loose the original exception, else try hack approach
|
||||
for py27."""
|
||||
messages = []
|
||||
formatted_ex = ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)) # noqa
|
||||
try:
|
||||
messages = [str(msg) for msg in
|
||||
_strip_newline_output_events(session.received)]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
messages = os.linesep.join(messages)
|
||||
fmt = {
|
||||
"sep": os.linesep,
|
||||
"messages": os.linesep.join(messages),
|
||||
"error": ''.join(traceback.format_exception_only(exc_type, exc_value)) # noqa
|
||||
}
|
||||
message = """
|
||||
|
||||
Session Messages:
|
||||
-----------------
|
||||
%(messages)s
|
||||
|
||||
Original Error:
|
||||
---------------
|
||||
%(error)s""" % fmt
|
||||
|
||||
try:
|
||||
raise Exception(messages) from ex
|
||||
except Exception:
|
||||
print(messages)
|
||||
raise ex
|
||||
# Chain the original exception for py3.
|
||||
exec('raise Exception(message) from ex', globals(), locals())
|
||||
except SyntaxError:
|
||||
# This happens when using py27.
|
||||
message = message + os.linesep + formatted_ex
|
||||
exec("raise Exception(message)", globals(), locals())
|
||||
|
||||
def _handle_exception(ex, adapter, session):
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
_kill_proc(adapter.pid)
|
||||
_wrap_and_reraise(ex, session)
|
||||
_wrap_and_reraise(session, ex, exc_type, exc_value, exc_traceback)
|
||||
|
||||
if debug_info.attachtype == 'import' and \
|
||||
debug_info.modulename is not None:
|
||||
|
|
|
|||
|
|
@ -111,10 +111,10 @@ class LaunchFileTests(BasicTests):
|
|||
)
|
||||
|
||||
|
||||
class LaunchModuleTests(BasicTests):
|
||||
class LaunchPackageTests(BasicTests):
|
||||
|
||||
def test_with_output(self):
|
||||
module_name = 'mymod_launch1'
|
||||
module_name = 'mypkg_launch1'
|
||||
cwd = WITH_OUTPUT.root
|
||||
env = WITH_OUTPUT.env_with_py_path()
|
||||
self.run_test_output(
|
||||
|
|
@ -122,7 +122,7 @@ class LaunchModuleTests(BasicTests):
|
|||
)
|
||||
|
||||
def test_without_output(self):
|
||||
module_name = 'mymod_launch1'
|
||||
module_name = 'mypkg_launch1'
|
||||
cwd = WITHOUT_OUTPUT.root
|
||||
env = WITHOUT_OUTPUT.env_with_py_path()
|
||||
self.run_test_without_output(
|
||||
|
|
@ -131,7 +131,7 @@ class LaunchModuleTests(BasicTests):
|
|||
|
||||
@unittest.skip('Broken')
|
||||
def test_termination(self):
|
||||
module_name = 'mymod_launch1'
|
||||
module_name = 'mypkg_launch1'
|
||||
cwd = TEST_TERMINATION_FILES.root
|
||||
env = TEST_TERMINATION_FILES.env_with_py_path()
|
||||
self.run_test_output(
|
||||
|
|
@ -143,7 +143,7 @@ class LaunchModuleTests(BasicTests):
|
|||
|
||||
@unittest.skip('Broken')
|
||||
def test_arguments(self):
|
||||
module_name = 'mymod_launch1'
|
||||
module_name = 'mypkg_launch1'
|
||||
cwd = WITH_ARGS.root
|
||||
env = WITH_ARGS.env_with_py_path()
|
||||
argv = ['arg1', 'arg2']
|
||||
|
|
@ -213,10 +213,10 @@ class PTVSDAttachTests(BasicTests):
|
|||
)
|
||||
|
||||
|
||||
class ServerAttachModuleTests(BasicTests):
|
||||
class ServerAttachPackageTests(BasicTests):
|
||||
|
||||
def test_with_output(self):
|
||||
module_name = 'mymod_launch1'
|
||||
module_name = 'mypkg_launch1'
|
||||
cwd = WITH_OUTPUT.root
|
||||
env = WITH_OUTPUT.env_with_py_path()
|
||||
argv = ['localhost', str(PORT)]
|
||||
|
|
@ -231,7 +231,7 @@ class ServerAttachModuleTests(BasicTests):
|
|||
)
|
||||
|
||||
def test_without_output(self):
|
||||
module_name = 'mymod_launch1'
|
||||
module_name = 'mypkg_launch1'
|
||||
cwd = WITHOUT_OUTPUT.root
|
||||
env = WITHOUT_OUTPUT.env_with_py_path()
|
||||
argv = ['localhost', str(PORT)]
|
||||
|
|
@ -246,11 +246,11 @@ class ServerAttachModuleTests(BasicTests):
|
|||
)
|
||||
|
||||
|
||||
class PTVSDAttachModuleTests(BasicTests):
|
||||
class PTVSDAttachPackageTests(BasicTests):
|
||||
|
||||
def test_with_output(self):
|
||||
#self.enable_verbose()
|
||||
module_name = 'mymod_attach1'
|
||||
module_name = 'mypkg_attach1'
|
||||
cwd = WITH_OUTPUT.root
|
||||
env = WITH_OUTPUT.env_with_py_path()
|
||||
argv = ['localhost', str(PORT)]
|
||||
|
|
@ -266,7 +266,7 @@ class PTVSDAttachModuleTests(BasicTests):
|
|||
)
|
||||
|
||||
def test_without_output(self):
|
||||
module_name = 'mymod_attach1'
|
||||
module_name = 'mypkg_attach1'
|
||||
cwd = WITHOUT_OUTPUT.root
|
||||
env = WITHOUT_OUTPUT.env_with_py_path()
|
||||
argv = ['localhost', str(PORT)]
|
||||
|
|
|
|||
|
|
@ -15,7 +15,17 @@ TEST_FILES = TestResources.from_module(__name__)
|
|||
class BreakpointTests(LifecycleTestsBase):
|
||||
|
||||
def run_test_with_break_points(self, debug_info, bp_filename, bp_line):
|
||||
options = {'debugOptions': ['RedirectOutput']}
|
||||
pathMappings = []
|
||||
# Required to ensure sourceReference = 0
|
||||
if (debug_info.starttype == 'attach'):
|
||||
pathMappings.append({
|
||||
'localRoot': debug_info.cwd,
|
||||
'remoteRoot': debug_info.cwd
|
||||
})
|
||||
options = {
|
||||
'debugOptions': ['RedirectOutput'],
|
||||
'pathMappings': pathMappings
|
||||
}
|
||||
breakpoints = [{
|
||||
'source': {
|
||||
'path': bp_filename
|
||||
|
|
@ -82,7 +92,7 @@ class BreakpointTests(LifecycleTestsBase):
|
|||
|
||||
def run_test_with_break_points_across_files(
|
||||
self, debug_info, first_file, second_file, second_file_line,
|
||||
expected_modules, expected_stacktrace):
|
||||
expected_stacktrace):
|
||||
breakpoints = [{
|
||||
'source': {
|
||||
'path': second_file
|
||||
|
|
@ -111,14 +121,6 @@ class BreakpointTests(LifecycleTestsBase):
|
|||
|
||||
session.send_request('continue', threadId=tid)
|
||||
|
||||
received = list(_strip_newline_output_events(session.received))
|
||||
|
||||
for mod in expected_modules:
|
||||
found_mod = self.find_events(received, 'module', mod)
|
||||
self.assertEqual(len(found_mod),
|
||||
1,
|
||||
'Modul not found {}'.format(mod))
|
||||
|
||||
self.assert_is_subset(stacktrace, expected_stacktrace)
|
||||
|
||||
def run_test_conditional_break_points(self, debug_info):
|
||||
|
|
@ -289,19 +291,6 @@ class LaunchFileTests(BreakpointTests):
|
|||
first_file = TEST_FILES.resolve('foo.py')
|
||||
second_file = TEST_FILES.resolve('bar.py')
|
||||
cwd = os.path.dirname(first_file)
|
||||
expected_modules = [{
|
||||
'reason': 'new',
|
||||
'module': {
|
||||
'path': second_file,
|
||||
'name': 'bar'
|
||||
}
|
||||
}, {
|
||||
'reason': 'new',
|
||||
'module': {
|
||||
'path': first_file,
|
||||
'name': '__main__'
|
||||
}
|
||||
}]
|
||||
expected_stacktrace = {
|
||||
'stackFrames': [{
|
||||
'name': 'do_bar',
|
||||
|
|
@ -335,7 +324,6 @@ class LaunchFileTests(BreakpointTests):
|
|||
first_file,
|
||||
second_file,
|
||||
2,
|
||||
expected_modules,
|
||||
expected_stacktrace,
|
||||
)
|
||||
|
||||
|
|
@ -422,10 +410,10 @@ class LaunchFileTests(BreakpointTests):
|
|||
)
|
||||
|
||||
|
||||
class LaunchModuleTests(BreakpointTests):
|
||||
class LaunchPackageTests(BreakpointTests):
|
||||
|
||||
def test_with_break_points(self):
|
||||
module_name = 'mymod_launch1'
|
||||
module_name = 'mypkg_launch1'
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
cwd = TEST_FILES.root
|
||||
bp_filename = os.path.join(cwd, module_name, '__init__.py')
|
||||
|
|
@ -436,25 +424,11 @@ class LaunchModuleTests(BreakpointTests):
|
|||
)
|
||||
|
||||
def test_with_break_points_across_files(self):
|
||||
module_name = 'mymod_foo'
|
||||
module_name = 'mypkg_foo'
|
||||
first_file = TEST_FILES.resolve(module_name, '__init__.py')
|
||||
second_file = TEST_FILES.resolve('mymod_bar', 'bar.py')
|
||||
second_file = TEST_FILES.resolve('mypkg_bar', 'bar.py')
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
cwd = TEST_FILES.root
|
||||
expected_modules = [{
|
||||
'reason': 'new',
|
||||
'module': {
|
||||
'package': 'mymod_bar',
|
||||
'path': second_file,
|
||||
'name': 'mymod_bar.bar'
|
||||
}
|
||||
}, {
|
||||
'reason': 'new',
|
||||
'module': {
|
||||
'path': first_file,
|
||||
'name': '__main__'
|
||||
}
|
||||
}]
|
||||
expected_stacktrace = {
|
||||
'stackFrames': [{
|
||||
'name': 'do_bar',
|
||||
|
|
@ -488,7 +462,6 @@ class LaunchModuleTests(BreakpointTests):
|
|||
first_file,
|
||||
second_file,
|
||||
2,
|
||||
expected_modules,
|
||||
expected_stacktrace,
|
||||
)
|
||||
|
||||
|
|
@ -530,10 +503,10 @@ class PTVSDAttachTests(BreakpointTests):
|
|||
)
|
||||
|
||||
|
||||
class ServerAttachModuleTests(BreakpointTests):
|
||||
class ServerAttachPackageTests(BreakpointTests):
|
||||
|
||||
def test_with_break_points(self):
|
||||
module_name = 'mymod_launch1'
|
||||
module_name = 'mypkg_launch1'
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
cwd = TEST_FILES.root
|
||||
argv = ['localhost', str(PORT)]
|
||||
|
|
@ -552,10 +525,10 @@ class ServerAttachModuleTests(BreakpointTests):
|
|||
|
||||
|
||||
@unittest.skip('Needs fixing')
|
||||
class PTVSDAttachModuleTests(BreakpointTests):
|
||||
class PTVSDAttachPackageTests(BreakpointTests):
|
||||
|
||||
def test_with_break_points(self):
|
||||
module_name = 'mymod_attach1'
|
||||
module_name = 'mypkg_attach1'
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
cwd = TEST_FILES.root
|
||||
argv = ['localhost', str(PORT)]
|
||||
|
|
|
|||
|
|
@ -100,14 +100,14 @@ class LaunchFileTests(ExceptionTests):
|
|||
class LaunchModuleExceptionLifecycleTests(ExceptionTests):
|
||||
|
||||
def test_breaking_into_handled_exceptions(self):
|
||||
module_name = 'mymod_launch1'
|
||||
module_name = 'mypkg_launch1'
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
cwd = TEST_FILES.parent.root
|
||||
self.run_test_breaking_into_handled_exceptions(
|
||||
DebugInfo(modulename=module_name, env=env, cwd=cwd))
|
||||
|
||||
def test_not_breaking_into_handled_exceptions(self):
|
||||
module_name = 'mymod_launch1'
|
||||
module_name = 'mypkg_launch1'
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
cwd = TEST_FILES.parent.root
|
||||
self.run_test_not_breaking_into_handled_exceptions(
|
||||
|
|
@ -174,7 +174,7 @@ class PTVSDAttachExceptionLifecycleTests(ExceptionTests):
|
|||
class ServerAttachModuleExceptionLifecycleTests(ExceptionTests):
|
||||
|
||||
def test_breaking_into_handled_exceptions(self):
|
||||
module_name = 'mymod_launch1'
|
||||
module_name = 'mypkg_launch1'
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
cwd = TEST_FILES.root
|
||||
argv = ['localhost', str(PORT)]
|
||||
|
|
@ -188,7 +188,7 @@ class ServerAttachModuleExceptionLifecycleTests(ExceptionTests):
|
|||
))
|
||||
|
||||
def test_not_breaking_into_handled_exceptions(self):
|
||||
module_name = 'mymod_launch1'
|
||||
module_name = 'mypkg_launch1'
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
cwd = TEST_FILES.root
|
||||
argv = ['localhost', str(PORT)]
|
||||
|
|
@ -206,7 +206,7 @@ class ServerAttachModuleExceptionLifecycleTests(ExceptionTests):
|
|||
class PTVSDAttachModuleExceptionLifecycleTests(ExceptionTests):
|
||||
|
||||
def test_breaking_into_handled_exceptions(self):
|
||||
module_name = 'mymod_attach1'
|
||||
module_name = 'mypkg_attach1'
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
cwd = TEST_FILES.root
|
||||
argv = ['localhost', str(PORT)]
|
||||
|
|
@ -221,7 +221,7 @@ class PTVSDAttachModuleExceptionLifecycleTests(ExceptionTests):
|
|||
))
|
||||
|
||||
def test_not_breaking_into_handled_exceptions(self):
|
||||
module_name = 'mymod_attach1'
|
||||
module_name = 'mypkg_attach1'
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
cwd = TEST_FILES.root
|
||||
argv = ['localhost', str(PORT)]
|
||||
|
|
|
|||
|
|
@ -474,29 +474,6 @@ class LifecycleTests(LifecycleTestsBase):
|
|||
'name': 'MainThread',
|
||||
}],
|
||||
}),
|
||||
self.new_event(
|
||||
'module',
|
||||
module={
|
||||
'id': 1,
|
||||
'name': '__main__',
|
||||
'path': filename,
|
||||
'package': None,
|
||||
},
|
||||
reason='new',
|
||||
),
|
||||
self.new_response(req_stacktrace1.req, **{
|
||||
'totalFrames': 1,
|
||||
'stackFrames': [{
|
||||
'id': 1,
|
||||
'name': '<module>',
|
||||
'source': {
|
||||
'path': filename,
|
||||
'sourceReference': 0,
|
||||
},
|
||||
'line': bp1,
|
||||
'column': 1,
|
||||
}],
|
||||
}),
|
||||
self.new_response(req_disconnect.req),
|
||||
])
|
||||
self.messages.reset_all()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import os
|
||||
import os.path
|
||||
import signal
|
||||
import sys
|
||||
import time
|
||||
|
||||
from tests.helpers.debugsession import Awaitable
|
||||
from tests.helpers.resource import TestResources
|
||||
from tests.helpers.socket import resolve_hostname
|
||||
from . import (
|
||||
|
|
@ -11,9 +15,20 @@ from . import (
|
|||
|
||||
TEST_FILES = TestResources.from_module('tests.system_tests.test_basic')
|
||||
WITH_OUTPUT = TEST_FILES.sub('test_output')
|
||||
SYSTEM_TEST_FILES = TestResources.from_module('tests.system_tests')
|
||||
WITH_TEST_FORVER = SYSTEM_TEST_FILES.sub('test_forever')
|
||||
|
||||
|
||||
class RemoteTests(LifecycleTestsBase):
|
||||
def _assert_stacktrace_is_subset(self, stacktrace, expected_stacktrace):
|
||||
# Ignore path case on Windows.
|
||||
if sys.platform == 'win32':
|
||||
for frame in stacktrace.get('stackFrames'):
|
||||
frame['source']['path'] = frame['source'].get('path', '').upper() # noqa
|
||||
for frame in expected_stacktrace.get('stackFrames'):
|
||||
frame['source']['path'] = frame['source'].get('path', '').upper() # noqa
|
||||
|
||||
self.assert_is_subset(stacktrace, expected_stacktrace)
|
||||
|
||||
def run_test_attach(self, debug_info):
|
||||
options = {'debugOptions': ['RedirectOutput']}
|
||||
|
|
@ -28,6 +43,48 @@ class RemoteTests(LifecycleTestsBase):
|
|||
self.new_event('output', category='stderr', output='no'),
|
||||
])
|
||||
|
||||
def run_test_source_references(self,
|
||||
debug_info,
|
||||
expected_stacktrace,
|
||||
path_mappings=[],
|
||||
debug_options=[]):
|
||||
options = {
|
||||
'debugOptions': debug_options,
|
||||
'pathMappings': path_mappings
|
||||
}
|
||||
|
||||
with self.start_debugging(debug_info) as dbg:
|
||||
(_, req_attach, _, _, _, req_threads) = lifecycle_handshake(
|
||||
dbg.session,
|
||||
debug_info.starttype,
|
||||
options=options,
|
||||
threads=True)
|
||||
|
||||
# wait till we enter the for loop.
|
||||
time.sleep(1)
|
||||
Awaitable.wait_all(req_attach, req_threads)
|
||||
with dbg.session.wait_for_event('stopped') as result:
|
||||
arguments = {
|
||||
'source': {
|
||||
'name': os.path.basename(debug_info.filename),
|
||||
'path': debug_info.filename
|
||||
},
|
||||
'lines': [9],
|
||||
'breakpoints': [{'line': 9}]
|
||||
}
|
||||
dbg.session.send_request('setBreakpoints', **arguments)
|
||||
|
||||
tid = result['msg'].body['threadId']
|
||||
stacktrace = dbg.session.send_request('stackTrace', threadId=tid)
|
||||
stacktrace.wait()
|
||||
dbg.session.send_request('continue', threadId=tid).wait()
|
||||
|
||||
# Kill remove program.
|
||||
os.kill(dbg.adapter.pid, signal.SIGTERM)
|
||||
|
||||
self._assert_stacktrace_is_subset(stacktrace.resp.body,
|
||||
expected_stacktrace)
|
||||
|
||||
|
||||
class AttachFileTests(RemoteTests):
|
||||
|
||||
|
|
@ -86,6 +143,129 @@ class AttachFileTests(RemoteTests):
|
|||
host=ip,
|
||||
cwd=cwd,
|
||||
starttype='attach',
|
||||
argv=argv,
|
||||
),
|
||||
)
|
||||
argv=argv))
|
||||
|
||||
def test_source_references_should_be_returned_without_path_mappings(self):
|
||||
filename = WITH_TEST_FORVER.resolve('attach_forever.py')
|
||||
cwd = os.path.dirname(filename)
|
||||
argv = ['localhost', str(PORT)]
|
||||
expected_stacktrace = {
|
||||
'stackFrames': [{
|
||||
'source': {
|
||||
'path': filename,
|
||||
'sourceReference': 1
|
||||
}
|
||||
}],
|
||||
}
|
||||
self.run_test_source_references(
|
||||
DebugInfo(
|
||||
filename=filename,
|
||||
attachtype='import',
|
||||
cwd=cwd,
|
||||
starttype='attach',
|
||||
argv=argv), expected_stacktrace)
|
||||
|
||||
def test_source_references_should_not_be_returned_with_path_mappings(self):
|
||||
filename = WITH_TEST_FORVER.resolve('attach_forever.py')
|
||||
cwd = os.path.dirname(filename)
|
||||
argv = ['localhost', str(PORT)]
|
||||
path_mappings = [{
|
||||
'localRoot': os.path.dirname(filename),
|
||||
'remoteRoot': os.path.dirname(filename)
|
||||
}]
|
||||
expected_stacktrace = {
|
||||
'stackFrames': [{
|
||||
'source': {
|
||||
'path': filename,
|
||||
'sourceReference': 0
|
||||
}
|
||||
}],
|
||||
}
|
||||
self.run_test_source_references(
|
||||
DebugInfo(
|
||||
filename=filename,
|
||||
attachtype='import',
|
||||
cwd=cwd,
|
||||
starttype='attach',
|
||||
argv=argv), expected_stacktrace, path_mappings)
|
||||
|
||||
def test_source_references_should_be_returned_with_invalid_path_mappings(
|
||||
self):
|
||||
filename = WITH_TEST_FORVER.resolve('attach_forever.py')
|
||||
cwd = os.path.dirname(filename)
|
||||
argv = ['localhost', str(PORT)]
|
||||
path_mappings = [{
|
||||
'localRoot': os.path.dirname(__file__),
|
||||
'remoteRoot': os.path.dirname(__file__)
|
||||
}]
|
||||
expected_stacktrace = {
|
||||
'stackFrames': [{
|
||||
'source': {
|
||||
'path': filename,
|
||||
'sourceReference': 1
|
||||
}
|
||||
}],
|
||||
}
|
||||
self.run_test_source_references(
|
||||
DebugInfo(
|
||||
filename=filename,
|
||||
attachtype='import',
|
||||
cwd=cwd,
|
||||
starttype='attach',
|
||||
argv=argv), expected_stacktrace, path_mappings)
|
||||
|
||||
def test_source_references_should_be_returned_with_win_client(self):
|
||||
filename = WITH_TEST_FORVER.resolve('attach_forever.py')
|
||||
cwd = os.path.dirname(filename)
|
||||
argv = ['localhost', str(PORT)]
|
||||
client_dir = 'C:\\Development\\Projects\\src\\sub dir'
|
||||
path_mappings = [{
|
||||
'localRoot': client_dir,
|
||||
'remoteRoot': os.path.dirname(filename)
|
||||
}]
|
||||
expected_stacktrace = {
|
||||
'stackFrames': [{
|
||||
'source': {
|
||||
'path': client_dir + '\\' + os.path.basename(filename),
|
||||
'sourceReference': 0
|
||||
}
|
||||
}],
|
||||
}
|
||||
self.run_test_source_references(
|
||||
DebugInfo(
|
||||
filename=filename,
|
||||
attachtype='import',
|
||||
cwd=cwd,
|
||||
starttype='attach',
|
||||
argv=argv),
|
||||
expected_stacktrace,
|
||||
path_mappings=path_mappings,
|
||||
debug_options=['WindowsClient'])
|
||||
|
||||
def test_source_references_should_be_returned_with_unix_client(self):
|
||||
filename = WITH_TEST_FORVER.resolve('attach_forever.py')
|
||||
cwd = os.path.dirname(filename)
|
||||
argv = ['localhost', str(PORT)]
|
||||
client_dir = '/Users/PeterSmith/projects/src/sub dir'
|
||||
path_mappings = [{
|
||||
'localRoot': client_dir,
|
||||
'remoteRoot': os.path.dirname(filename)
|
||||
}]
|
||||
expected_stacktrace = {
|
||||
'stackFrames': [{
|
||||
'source': {
|
||||
'path': client_dir + '/' + os.path.basename(filename),
|
||||
'sourceReference': 0
|
||||
}
|
||||
}],
|
||||
}
|
||||
self.run_test_source_references(
|
||||
DebugInfo(
|
||||
filename=filename,
|
||||
attachtype='import',
|
||||
cwd=cwd,
|
||||
starttype='attach',
|
||||
argv=argv),
|
||||
expected_stacktrace,
|
||||
path_mappings=path_mappings,
|
||||
debug_options=['UnixClient'])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue