Fix path mappings with remote debugging (#624)

Fixes #610
This commit is contained in:
Don Jayamanne 2018-07-10 15:09:16 -07:00 committed by GitHub
parent 2b0140e22c
commit 14982d90ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 317 additions and 148 deletions

View file

@ -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": [
{

View file

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

View file

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

View file

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

View file

@ -1,8 +0,0 @@
import mymod_bar.bar
def do_foo():
mymod_bar.bar.do_bar()
do_foo()

View file

@ -0,0 +1,8 @@
import mypkg_bar.bar
def do_foo():
mypkg_bar.bar.do_bar()
do_foo()

View 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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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