Skip to content

Commit

Permalink
Fix issue where working dir becomes wrong on subst drive on Windows. F…
Browse files Browse the repository at this point in the history
  • Loading branch information
fabioz committed Feb 13, 2020
1 parent dbae5a7 commit 45e8e99
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 5 deletions.
1 change: 1 addition & 0 deletions changelog/5965.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
No longer resolve symlinks so that the current directory remains correct even with drives mapped with subst on Windows.
12 changes: 9 additions & 3 deletions src/_pytest/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ def get_config(args=None, plugins=None):
config = Config(
pluginmanager,
invocation_params=Config.InvocationParams(
args=args or (), plugins=plugins, dir=Path().resolve()
args=args or (), plugins=plugins, dir=Path.cwd()
),
)

Expand Down Expand Up @@ -462,7 +462,13 @@ def _getconftestmodules(self, path):
# and allow users to opt into looking into the rootdir parent
# directories instead of requiring to specify confcutdir
clist = []
for parent in directory.realpath().parts():

p = directory
if sys.platform != 'win32':
# Only resolve symlinks if not on windows. See: #5965
p = p.realpath()

for parent in p.parts():
if self._confcutdir and self._confcutdir.relto(parent):
continue
conftestpath = parent.join("conftest.py")
Expand Down Expand Up @@ -771,7 +777,7 @@ def __init__(self, pluginmanager, *, invocation_params=None) -> None:

if invocation_params is None:
invocation_params = self.InvocationParams(
args=(), plugins=None, dir=Path().resolve()
args=(), plugins=None, dir=Path.cwd()
)

self.option = argparse.Namespace()
Expand Down
5 changes: 4 additions & 1 deletion src/_pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -1288,7 +1288,10 @@ def getfixtureinfo(self, node, func, cls, funcargs=True):
def pytest_plugin_registered(self, plugin):
nodeid = None
try:
p = py.path.local(plugin.__file__).realpath()
p = py.path.local(plugin.__file__)
if sys.platform != 'win32':
# Only resolve symlinks if not on windows. See: #5965
p = p.realpath()
except AttributeError:
pass
else:
Expand Down
6 changes: 5 additions & 1 deletion src/_pytest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,11 @@ def _parsearg(self, arg):
"file or package not found: " + arg + " (missing __init__.py?)"
)
raise UsageError("file not found: " + arg)
fspath = fspath.realpath()

if sys.platform != 'win32':
# Only resolve symlinks if not on windows. See: #5965
fspath = fspath.realpath()

return (fspath, parts)

def matchnodes(self, matching, names):
Expand Down
95 changes: 95 additions & 0 deletions testing/test_windows_subst.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import os.path
import subprocess
import sys
from string import ascii_lowercase

from py._path.local import LocalPath

import pytest
from _pytest import pytester
from _pytest.fixtures import FixtureRequest


class _SubstTmpdirFactory:
"""
A temp dir factory which may do a subst on Windows drives.
"""

def __init__(self, tmpdir, tmpdir_factory, subst):
self.tmpdir = tmpdir
self.tmpdir_factory = tmpdir_factory
self.subst = subst
self.subst_drives = {}

def _subst_path(self, filename):

if self.subst:
for c in ascii_lowercase[7:]: # Create a subst drive from H-Z.
c += ":"
if not os.path.exists(c):
drive = c
break
else:
raise AssertionError("Unable to find suitable drive letter for subst.")

directory = os.path.dirname(filename)
basename = os.path.basename(filename)

args = ["subst", drive, directory]
subprocess.check_call(args)
assert os.path.exists(drive)
self.subst_drives[drive] = directory

filename = LocalPath(os.path.join(drive, basename))

return filename

def mktemp(self, name, numbered):
filename = self.tmpdir_factory.mktemp(name, numbered=numbered)
filename = self._subst_path(filename)
return filename

def unsubst(self):
for drive in self.subst_drives:
args = ["subst", "/D", drive]
subprocess.check_call(args)


@pytest.fixture(params=[{"subst": True}, {"subst": False}])
def _subst_tmpdir_factory(request: FixtureRequest, tmpdir, tmpdir_factory):
if sys.platform != "win32":
pytest.skip("Windows only test.")
factory = _SubstTmpdirFactory(tmpdir, tmpdir_factory, **request.param)
yield factory
factory.unsubst()


@pytest.fixture
def _custom_testdir(
request: FixtureRequest, _subst_tmpdir_factory
) -> "pytester.Testdir":
return pytester.Testdir(request, _subst_tmpdir_factory)


def test_windows_subst_resolve(_custom_testdir, _subst_tmpdir_factory) -> None:
"""
Check that when we have a subst on Windows the errors reported are not
in the subst target.
See: https://github.com/pytest-dev/pytest/issues/5965
"""
testdir = _custom_testdir
testdir.makepyfile(
"""
import pytest
def test_foo():
raise AssertionError()
"""
)
result = testdir.runpytest()

# i.e.: Make sure that the error is reported as a relative path, not as an
# absolute (resolved) path.
# See: https://github.com/pytest-dev/pytest/issues/5965
for _drive, directory in _subst_tmpdir_factory.subst_drives.items():
for line in result.stdout.lines:
assert directory.lower() not in line.lower()

0 comments on commit 45e8e99

Please sign in to comment.