diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 4cbaebeb1a4..fef057a1c6e 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -762,21 +762,13 @@ def test(): result = testdir.runpytest(str(p) + "::test", "--doctest-modules") result.stdout.fnmatch_lines(["*1 passed*"]) - def test_cmdline_python_package_symlink(self, testdir, monkeypatch): + def test_cmdline_python_package_symlink( + self, testdir, monkeypatch, symlink_or_skip + ): """ test --pyargs option with packages with path containing symlink can have conftest.py in their package (#2985) """ - # dummy check that we can actually create symlinks: on Windows `os.symlink` is available, - # but normal users require special admin privileges to create symlinks. - if sys.platform == "win32": - try: - os.symlink( - str(testdir.tmpdir.ensure("tmpfile")), - str(testdir.tmpdir.join("tmpfile2")), - ) - except OSError as e: - pytest.skip(str(e.args[0])) monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False) dirname = "lib" @@ -794,7 +786,11 @@ def test_cmdline_python_package_symlink(self, testdir, monkeypatch): d_local = testdir.mkdir("local") symlink_location = os.path.join(str(d_local), "lib") - os.symlink(str(d), symlink_location, target_is_directory=True) + try: + symlink_or_skip(str(d), symlink_location, target_is_directory=True) + has_symlinks = True + except pytest.skip.Exception: + has_symlinks = False # The structure of the test directory is now: # . @@ -816,22 +812,21 @@ def test_cmdline_python_package_symlink(self, testdir, monkeypatch): # module picked up in symlink-ed directory: # It picks up local/lib/foo/bar (symlink) via sys.path. - result = testdir.runpytest("--pyargs", "-v", "foo.bar") - testdir.chdir() + result = testdir.runpytest("--pyargs", "-vv", "foo.bar") assert result.ret == 0 - if hasattr(py.path.local, "mksymlinkto"): + if has_symlinks: result.stdout.fnmatch_lines( [ - "lib/foo/bar/test_bar.py::test_bar PASSED*", - "lib/foo/bar/test_bar.py::test_other PASSED*", + "lib/foo/bar/test_bar.py::test_bar <- local/lib/foo/bar/test_bar.py PASSED*", + "lib/foo/bar/test_bar.py::test_other <- local/lib/foo/bar/test_bar.py PASSED*", "*2 passed*", ] ) else: result.stdout.fnmatch_lines( [ - "*lib/foo/bar/test_bar.py::test_bar PASSED*", - "*lib/foo/bar/test_bar.py::test_other PASSED*", + "local/lib/foo/bar/test_bar.py::test_bar PASSED*", + "local/lib/foo/bar/test_bar.py::test_other PASSED*", "*2 passed*", ] ) diff --git a/testing/conftest.py b/testing/conftest.py index 90cdcb869fd..02e095cb2c9 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -1,3 +1,4 @@ +import os import re import sys from typing import List @@ -136,6 +137,24 @@ def testdir(testdir: Testdir) -> Testdir: return testdir +@pytest.fixture +def symlink_or_skip(): + """Return a function that creates a symlink or raises ``Skip``. + + On Windows `os.symlink` is available, but normal users require special + admin privileges to create symlinks. + """ + + def wrap_os_symlink(src, dst, *args, **kwargs): + try: + os.symlink(src, dst, *args, **kwargs) + except OSError as e: + pytest.skip("os.symlink({!r}) failed: {!r}".format((src, dst), e)) + assert os.path.islink(dst) + + return wrap_os_symlink + + @pytest.fixture(scope="session") def color_mapping(): """Returns a utility class which can replace keys in strings in the form "{NAME}"