Repository: microsoft/ptvsd

Issue:
Source references not working
## Environment data

- PTVSD version: master
- OS and version: windows to windows
- Python version (& distribution if applicable, e.g. Anaconda): 3.7
- Using VS Code or Visual Studio: VSC

## Actual behavior

Pausing while using remote only file (no path mapping) will bring up:
![image](https://user-images.githubusercontent.com/3840081/42406222-d3f69c9e-8157-11e8-97b1-844c82ea57e8.png)


## Expected  behavior

Should use source reference to pull the remote file

## Steps to reproduce:

### On remote machine:
1. Create this file `remote_test.py`:
```python
import time
import ptvsd

ptvsd.enable_attach()

i = 0
while True:
    print(i)
    time.sleep(1)
    i += 1
```
2. Ensure port `5678` has access for inbound connections.
3. run `py -3.7 remote_test.py`

### On dev machine:
1.  Use following launch config:
```json
        {
            "name": "PyExp: Attach (Remote)",
            "type": "pythonExperimental",
            "request": "attach",
            "port": 5678,
            "host": "<remote-pc-name>",
            "logToFile": true
        },
```
2. Select `PyExp: Attach (Remote)` to attach.
3. Hit pause after attaching.





Discussion:
@karthiknadig 
This is the PR i was referring to https://github.com/Microsoft/ptvsd/pull/533
See comment here https://github.com/Microsoft/ptvsd/issues/473#issuecomment-401464260
@karthiknadig 
As suspected, #533 broke this. In hindsight we shouldn't have merged the PR without testing this.

Anyways, I'll be looking into this.
There are a number of problems that are contributing to this issue.
I'll address the first item.

* `WINDOWS_CLIENT` is being set incorrectly
In `wrapper.py` we initialize a `debugOption` named `WINDOWS_CLIENT`.
Based on the original code, the default value of `WINDOWS_CLIENT` was `False`. The existence of the item `WindowsClient` would turn on the value for `WINDOWS_CLIENT`.
https://github.com/Microsoft/ptvsd/blob/master/ptvsd/wrapper.py#L730

Now, VSC is NOT sending this value, hence the value is expected to be false.

One thing from a quick look at the code is that CMD_LOAD_SOURCE was fixed on `pydevd` to convert the path to the server properly, but inside `ptvsd.wrapper.VSCodeMessageProcessor.on_source` it already used the private API to do a translation -- although it's probably not the culprit here it's an inconsistency added after #533 was merged.
@fabioz	I can see that #533 addresses a few issues we had in the past.
I think I've figured out what's going on, two fold, a bug at our end and we need to account for upstream pydevd changes (#533).

Resolving patch:
diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py
--- a/ptvsd/wrapper.py
+++ b/ptvsd/wrapper.py
@@ -713,7 +713,7 @@ def bool_parser(str):
     '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 @@ def bool_parser(str):
     '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 @@ def __init__(self, socket, pydevd_notify, pydevd_request,
         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 @@ def _should_debug(self, filepath):
         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 @@ def get_source_reference(self, filename):
         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
-
-        client_filename = pydevd_file_utils.norm_file_to_client(filename)
-
-        # 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)
+            pass
+
+        # 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
+
+        return self.source_map.to_vscode(filename, autogen=True)
 
     @async_handler
     def on_stackTrace(self, request, args):


Test patch:
diff --git a/tests/helpers/debugadapter.py b/tests/helpers/debugadapter.py
--- a/tests/helpers/debugadapter.py
+++ b/tests/helpers/debugadapter.py
@@ -53,6 +53,13 @@
 ]
 
 
+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?
diff --git a/tests/highlevel/test_lifecycle.py b/tests/highlevel/test_lifecycle.py
--- a/tests/highlevel/test_lifecycle.py
+++ b/tests/highlevel/test_lifecycle.py
@@ -125,12 +125,20 @@ def test_attach_exit_during_session(self):
         # TODO: Ensure we see the "terminated" and "exited" events.
         raise NotImplementedError
 
+    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': 'WINDOWS_CLIENT=False'}
+        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):
diff --git a/tests/resources/system_tests/test_basic/test_args/mymod_launch1/__init__.py b/tests/resources/system_tests/test_basic/test_args/mypkg_launch1/__init__.py
similarity index 100%
rename from tests/resources/system_tests/test_basic/test_args/mymod_launch1/__init__.py
rename to tests/resources/system_tests/test_basic/test_args/mypkg_launch1/__init__.py
diff --git a/tests/resources/system_tests/test_basic/test_args/mymod_launch1/__main__.py b/tests/resources/system_tests/test_basic/test_args/mypkg_launch1/__main__.py
similarity index 100%
rename from tests/resources/system_tests/test_basic/test_args/mymod_launch1/__main__.py
rename to tests/resources/system_tests/test_basic/test_args/mypkg_launch1/__main__.py
diff --git a/tests/resources/system_tests/test_basic/test_output/mymod_attach1/__init__.py b/tests/resources/system_tests/test_basic/test_output/mypkg_attach1/__init__.py
similarity index 100%
rename from tests/resources/system_tests/test_basic/test_output/mymod_attach1/__init__.py
rename to tests/resources/system_tests/test_basic/test_output/mypkg_attach1/__init__.py
diff --git a/tests/resources/system_tests/test_basic/test_output/mymod_attach1/__main__.py b/tests/resources/system_tests/test_basic/test_output/mypkg_attach1/__main__.py
similarity index 100%
rename from tests/resources/system_tests/test_basic/test_output/mymod_attach1/__main__.py
rename to tests/resources/system_tests/test_basic/test_output/mypkg_attach1/__main__.py
diff --git a/tests/resources/system_tests/test_basic/test_output/mymod_launch1/__init__.py b/tests/resources/system_tests/test_basic/test_output/mypkg_launch1/__init__.py
similarity index 100%
rename from tests/resources/system_tests/test_basic/test_output/mymod_launch1/__init__.py
rename to tests/resources/system_tests/test_basic/test_output/mypkg_launch1/__init__.py
diff --git a/tests/resources/system_tests/test_basic/test_output/mymod_launch1/__main__.py b/tests/resources/system_tests/test_basic/test_output/mypkg_launch1/__main__.py
similarity index 100%
rename from tests/resources/system_tests/test_basic/test_output/mymod_launch1/__main__.py
rename to tests/resources/system_tests/test_basic/test_output/mypkg_launch1/__main__.py
diff --git a/tests/resources/system_tests/test_basic/test_without_output/mymod_attach1/__init__.py b/tests/resources/system_tests/test_basic/test_without_output/mypkg_attach1/__init__.py
similarity index 100%
rename from tests/resources/system_tests/test_basic/test_without_output/mymod_attach1/__init__.py
rename to tests/resources/system_tests/test_basic/test_without_output/mypkg_attach1/__init__.py
diff --git a/tests/resources/system_tests/test_basic/test_without_output/mymod_attach1/__main__.py b/tests/resources/system_tests/test_basic/test_without_output/mypkg_attach1/__main__.py
similarity index 100%
rename from tests/resources/system_tests/test_basic/test_without_output/mymod_attach1/__main__.py
rename to tests/resources/system_tests/test_basic/test_without_output/mypkg_attach1/__main__.py
diff --git a/tests/resources/system_tests/test_basic/test_without_output/mymod_launch1/__init__.py b/tests/resources/system_tests/test_basic/test_without_output/mypkg_launch1/__init__.py
similarity index 100%
rename from tests/resources/system_tests/test_basic/test_without_output/mymod_launch1/__init__.py
rename to tests/resources/system_tests/test_basic/test_without_output/mypkg_launch1/__init__.py
diff --git a/tests/resources/system_tests/test_basic/test_without_output/mymod_launch1/__main__.py b/tests/resources/system_tests/test_basic/test_without_output/mypkg_launch1/__main__.py
similarity index 100%
rename from tests/resources/system_tests/test_basic/test_without_output/mymod_launch1/__main__.py
rename to tests/resources/system_tests/test_basic/test_without_output/mypkg_launch1/__main__.py
diff --git a/tests/resources/system_tests/test_breakpoints/mymod_foo/__init__.py b/tests/resources/system_tests/test_breakpoints/mymod_foo/__init__.py
deleted file mode 100644
--- a/tests/resources/system_tests/test_breakpoints/mymod_foo/__init__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import mymod_bar.bar
-
-
-def do_foo():
-    mymod_bar.bar.do_bar()
-
-
-do_foo()
diff --git a/tests/resources/system_tests/test_breakpoints/mymod_attach1/__init__.py b/tests/resources/system_tests/test_breakpoints/mypkg_attach1/__init__.py
similarity index 100%
rename from tests/resources/system_tests/test_breakpoints/mymod_attach1/__init__.py
rename to tests/resources/system_tests/test_breakpoints/mypkg_attach1/__init__.py
diff --git a/tests/resources/system_tests/test_breakpoints/mymod_attach1/__main__.py b/tests/resources/system_tests/test_breakpoints/mypkg_attach1/__main__.py
similarity index 100%
rename from tests/resources/system_tests/test_breakpoints/mymod_attach1/__main__.py
rename to tests/resources/system_tests/test_breakpoints/mypkg_attach1/__main__.py
diff --git a/tests/resources/system_tests/test_breakpoints/mymod_bar/__init__.py b/tests/resources/system_tests/test_breakpoints/mypkg_bar/__init__.py
similarity index 100%
rename from tests/resources/system_tests/test_breakpoints/mymod_bar/__init__.py
rename to tests/resources/system_tests/test_breakpoints/mypkg_bar/__init__.py
diff --git a/tests/resources/system_tests/test_breakpoints/mymod_bar/__main__.py b/tests/resources/system_tests/test_breakpoints/mypkg_bar/__main__.py
similarity index 100%
rename from tests/resources/system_tests/test_breakpoints/mymod_bar/__main__.py
rename to tests/resources/system_tests/test_breakpoints/mypkg_bar/__main__.py
diff --git a/tests/resources/system_tests/test_breakpoints/mymod_bar/bar.py b/tests/resources/system_tests/test_breakpoints/mypkg_bar/bar.py
similarity index 100%
rename from tests/resources/system_tests/test_breakpoints/mymod_bar/bar.py
rename to tests/resources/system_tests/test_breakpoints/mypkg_bar/bar.py
diff --git a/tests/resources/system_tests/test_breakpoints/mypkg_foo/__init__.py b/tests/resources/system_tests/test_breakpoints/mypkg_foo/__init__.py
new file mode 100644
--- /dev/null
+++ b/tests/resources/system_tests/test_breakpoints/mypkg_foo/__init__.py
@@ -0,0 +1,8 @@
+import mypkg_bar.bar
+
+
+def do_foo():
+    mypkg_bar.bar.do_bar()
+
+
+do_foo()
diff --git a/tests/resources/system_tests/test_breakpoints/mymod_foo/__main__.py b/tests/resources/system_tests/test_breakpoints/mypkg_foo/__main__.py
similarity index 100%
rename from tests/resources/system_tests/test_breakpoints/mymod_foo/__main__.py
rename to tests/resources/system_tests/test_breakpoints/mypkg_foo/__main__.py
diff --git a/tests/resources/system_tests/test_breakpoints/mymod_launch1/__init__.py b/tests/resources/system_tests/test_breakpoints/mypkg_launch1/__init__.py
similarity index 100%
rename from tests/resources/system_tests/test_breakpoints/mymod_launch1/__init__.py
rename to tests/resources/system_tests/test_breakpoints/mypkg_launch1/__init__.py
diff --git a/tests/resources/system_tests/test_breakpoints/mymod_launch1/__main__.py b/tests/resources/system_tests/test_breakpoints/mypkg_launch1/__main__.py
similarity index 100%
rename from tests/resources/system_tests/test_breakpoints/mymod_launch1/__main__.py
rename to tests/resources/system_tests/test_breakpoints/mypkg_launch1/__main__.py
diff --git a/tests/resources/system_tests/test_exceptions/mymod_attach1/__init__.py b/tests/resources/system_tests/test_exceptions/mypkg_attach1/__init__.py
similarity index 100%
rename from tests/resources/system_tests/test_exceptions/mymod_attach1/__init__.py
rename to tests/resources/system_tests/test_exceptions/mypkg_attach1/__init__.py
diff --git a/tests/resources/system_tests/test_exceptions/mymod_attach1/__main__.py b/tests/resources/system_tests/test_exceptions/mypkg_attach1/__main__.py
similarity index 100%
rename from tests/resources/system_tests/test_exceptions/mymod_attach1/__main__.py
rename to tests/resources/system_tests/test_exceptions/mypkg_attach1/__main__.py
diff --git a/tests/resources/system_tests/test_exceptions/mymod_launch1/__init__.py b/tests/resources/system_tests/test_exceptions/mypkg_launch1/__init__.py
similarity index 100%
rename from tests/resources/system_tests/test_exceptions/mymod_launch1/__init__.py
rename to tests/resources/system_tests/test_exceptions/mypkg_launch1/__init__.py
diff --git a/tests/resources/system_tests/test_exceptions/mymod_launch1/__main__.py b/tests/resources/system_tests/test_exceptions/mypkg_launch1/__main__.py
similarity index 100%
rename from tests/resources/system_tests/test_exceptions/mymod_launch1/__main__.py
rename to tests/resources/system_tests/test_exceptions/mypkg_launch1/__main__.py
diff --git a/tests/resources/system_tests/test_forever/attach_forever.py b/tests/resources/system_tests/test_forever/attach_forever.py
new file mode 100644
--- /dev/null
+++ b/tests/resources/system_tests/test_forever/attach_forever.py
@@ -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
diff --git a/tests/resources/system_tests/test_terminate/mymod_launch1/__init__.py b/tests/resources/system_tests/test_terminate/mypkg_launch1/__init__.py
similarity index 100%
rename from tests/resources/system_tests/test_terminate/mymod_launch1/__init__.py
rename to tests/resources/system_tests/test_terminate/mypkg_launch1/__init__.py
diff --git a/tests/resources/system_tests/test_terminate/mymod_launch1/__main__.py b/tests/resources/system_tests/test_terminate/mypkg_launch1/__main__.py
similarity index 100%
rename from tests/resources/system_tests/test_terminate/mymod_launch1/__main__.py
rename to tests/resources/system_tests/test_terminate/mypkg_launch1/__main__.py
diff --git a/tests/resources/system_tests/test_variables/mymod_attach1/__init__.py b/tests/resources/system_tests/test_variables/mypkg_attach1/__init__.py
similarity index 100%
rename from tests/resources/system_tests/test_variables/mymod_attach1/__init__.py
rename to tests/resources/system_tests/test_variables/mypkg_attach1/__init__.py
diff --git a/tests/resources/system_tests/test_variables/mymod_attach1/__main__.py b/tests/resources/system_tests/test_variables/mypkg_attach1/__main__.py
similarity index 100%
rename from tests/resources/system_tests/test_variables/mymod_attach1/__main__.py
rename to tests/resources/system_tests/test_variables/mypkg_attach1/__main__.py
diff --git a/tests/resources/system_tests/test_variables/mymod_launch1/__init__.py b/tests/resources/system_tests/test_variables/mypkg_launch1/__init__.py
similarity index 100%
rename from tests/resources/system_tests/test_variables/mymod_launch1/__init__.py
rename to tests/resources/system_tests/test_variables/mypkg_launch1/__init__.py
diff --git a/tests/resources/system_tests/test_variables/mymod_launch1/__main__.py b/tests/resources/system_tests/test_variables/mypkg_launch1/__main__.py
similarity index 100%
rename from tests/resources/system_tests/test_variables/mymod_launch1/__main__.py
rename to tests/resources/system_tests/test_variables/mypkg_launch1/__main__.py
diff --git a/tests/system_tests/__init__.py b/tests/system_tests/__init__.py
--- a/tests/system_tests/__init__.py
+++ b/tests/system_tests/__init__.py
@@ -2,7 +2,9 @@
 import os
 import ptvsd
 import signal
+import sys
 import time
+import traceback
 import unittest
 
 from collections import namedtuple
@@ -234,24 +236,46 @@ def _kill_proc(pid):
                 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:
diff --git a/tests/system_tests/test_basic.py b/tests/system_tests/test_basic.py
--- a/tests/system_tests/test_basic.py
+++ b/tests/system_tests/test_basic.py
@@ -111,10 +111,10 @@ def test_without_output(self):
         )
 
 
-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 @@ def test_with_output(self):
         )
 
     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 @@ def test_without_output(self):
 
     @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 @@ def test_termination(self):
 
     @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 @@ def test_without_output(self):
         )
 
 
-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 @@ def test_with_output(self):
         )
 
     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 @@ def test_without_output(self):
         )
 
 
-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 @@ def test_with_output(self):
         )
 
     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)]
diff --git a/tests/system_tests/test_breakpoints.py b/tests/system_tests/test_breakpoints.py
--- a/tests/system_tests/test_breakpoints.py
+++ b/tests/system_tests/test_breakpoints.py
@@ -15,7 +15,17 @@
 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 @@ def run_test_with_break_points(self, debug_info, bp_filename, bp_line):
 
     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 @@ def run_test_with_break_points_across_files(
 
             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 @@ def test_with_break_points_across_files(self):
         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 @@ def test_with_break_points_across_files(self):
             first_file,
             second_file,
             2,
-            expected_modules,
             expected_stacktrace,
         )
 
@@ -422,10 +410,10 @@ def test_hit_conditional_break_points_mod(self):
         )
 
 
-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 @@ def test_with_break_points(self):
         )
 
     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 @@ def test_with_break_points_across_files(self):
             first_file,
             second_file,
             2,
-            expected_modules,
             expected_stacktrace,
         )
 
@@ -530,10 +503,10 @@ def test_with_break_points(self):
         )
 
 
-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 @@ def test_with_break_points(self):
 
 
 @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)]
diff --git a/tests/system_tests/test_exceptions.py b/tests/system_tests/test_exceptions.py
--- a/tests/system_tests/test_exceptions.py
+++ b/tests/system_tests/test_exceptions.py
@@ -100,14 +100,14 @@ def test_breaking_into_handled_exceptions(self):
 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 @@ def test_not_breaking_into_handled_exceptions(self):
 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 @@ def test_breaking_into_handled_exceptions(self):
             ))
 
     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 @@ def test_not_breaking_into_handled_exceptions(self):
 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 @@ def test_breaking_into_handled_exceptions(self):
             ))
 
     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)]
diff --git a/tests/system_tests/test_main.py b/tests/system_tests/test_main.py
--- a/tests/system_tests/test_main.py
+++ b/tests/system_tests/test_main.py
@@ -474,29 +474,6 @@ def test_detach_clear_and_resume(self):
                     '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()
diff --git a/tests/system_tests/test_remote.py b/tests/system_tests/test_remote.py
--- a/tests/system_tests/test_remote.py
+++ b/tests/system_tests/test_remote.py
@@ -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 @@
 
 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 @@ def run_test_attach(self, debug_info):
             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 @@ def test_attach_byip(self):
                 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'])