From 5d791802c925425e75370e88c7d1e68f23b7d724 Mon Sep 17 00:00:00 2001
From: Antoine Pitrou <solipsis@pitrou.net>
Date: Wed, 23 Oct 2013 19:11:29 +0200
Subject: [PATCH] Issue #19352: Fix unittest discovery when a module can be
 reached through several paths (e.g. under Debian/Ubuntu with virtualenv).

---
 Lib/unittest/loader.py              |  4 ++--
 Lib/unittest/test/test_discovery.py | 24 +++++++++++++++++++++++-
 Misc/NEWS                           |  3 +++
 3 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/Lib/unittest/loader.py b/Lib/unittest/loader.py
index c6fc6630de1..9163a1a00d3 100644
--- a/Lib/unittest/loader.py
+++ b/Lib/unittest/loader.py
@@ -256,8 +256,8 @@ class TestLoader(object):
                     yield _make_failed_import_test(name, self.suiteClass)
                 else:
                     mod_file = os.path.abspath(getattr(module, '__file__', full_path))
-                    realpath = os.path.splitext(mod_file)[0]
-                    fullpath_noext = os.path.splitext(full_path)[0]
+                    realpath = os.path.splitext(os.path.realpath(mod_file))[0]
+                    fullpath_noext = os.path.splitext(os.path.realpath(full_path))[0]
                     if realpath.lower() != fullpath_noext.lower():
                         module_dir = os.path.dirname(realpath)
                         mod_name = os.path.splitext(os.path.basename(full_path))[0]
diff --git a/Lib/unittest/test/test_discovery.py b/Lib/unittest/test/test_discovery.py
index 8c031a0034a..e0277d5787e 100644
--- a/Lib/unittest/test/test_discovery.py
+++ b/Lib/unittest/test/test_discovery.py
@@ -314,7 +314,7 @@ class TestDiscovery(unittest.TestCase):
         self.assertTrue(program.failfast)
         self.assertTrue(program.catchbreak)
 
-    def test_detect_module_clash(self):
+    def setup_module_clash(self):
         class Module(object):
             __file__ = 'bar/foo.py'
         sys.modules['foo'] = Module
@@ -341,7 +341,10 @@ class TestDiscovery(unittest.TestCase):
         os.listdir = listdir
         os.path.isfile = isfile
         os.path.isdir = isdir
+        return full_path
 
+    def test_detect_module_clash(self):
+        full_path = self.setup_module_clash()
         loader = unittest.TestLoader()
 
         mod_dir = os.path.abspath('bar')
@@ -354,6 +357,25 @@ class TestDiscovery(unittest.TestCase):
         )
         self.assertEqual(sys.path[0], full_path)
 
+    def test_module_symlink_ok(self):
+        full_path = self.setup_module_clash()
+
+        original_realpath = os.path.realpath
+
+        mod_dir = os.path.abspath('bar')
+        expected_dir = os.path.abspath('foo')
+
+        def cleanup():
+            os.path.realpath = original_realpath
+        self.addCleanup(cleanup)
+
+        def realpath(path):
+            if path == os.path.join(mod_dir, 'foo.py'):
+                return os.path.join(expected_dir, 'foo.py')
+            return path
+        os.path.realpath = realpath
+        loader = unittest.TestLoader()
+        loader.discover(start_dir='foo', pattern='foo.py')
 
     def test_discovery_from_dotted_path(self):
         loader = unittest.TestLoader()
diff --git a/Misc/NEWS b/Misc/NEWS
index 3ee26437a84..24089dc2e8b 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -40,6 +40,9 @@ Core and Builtins
 Library
 -------
 
+- Issue #19352: Fix unittest discovery when a module can be reached
+  through several paths (e.g. under Debian/Ubuntu with virtualenv).
+
 - Issue #15207: Fix mimetypes to read from correct part of Windows registry
   Original patch by Dave Chambers
 
-- 
2.30.9