Merge pull request #10988 from nicoddemus/initial-testpaths-10987

Consider testpaths for initial conftests
This commit is contained in:
Bruno Oliveira 2023-05-12 09:58:59 -03:00 committed by GitHub
commit 76d15231f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 76 additions and 12 deletions

View File

@ -0,0 +1 @@
Fix bug where very long option names could cause pytest to break with ``OSError: [Errno 36] File name too long`` on some systems.

View File

@ -0,0 +1 @@
:confval:`testpaths` is now honored to load root ``conftests``.

View File

@ -1713,13 +1713,12 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: testpaths .. confval:: testpaths
Sets list of directories that should be searched for tests when Sets list of directories that should be searched for tests when
no specific directories, files or test ids are given in the command line when no specific directories, files or test ids are given in the command line when
executing pytest from the :ref:`rootdir <rootdir>` directory. executing pytest from the :ref:`rootdir <rootdir>` directory.
File system paths may use shell-style wildcards, including the recursive File system paths may use shell-style wildcards, including the recursive
``**`` pattern. ``**`` pattern.
Useful when all project tests are in a known location to speed up Useful when all project tests are in a known location to speed up
test collection and to avoid picking up undesired tests by accident. test collection and to avoid picking up undesired tests by accident.
@ -1728,8 +1727,17 @@ passed multiple times. The expected format is ``name=value``. For example::
[pytest] [pytest]
testpaths = testing doc testpaths = testing doc
This tells pytest to only look for tests in ``testing`` and ``doc`` This configuration means that executing:
directories when executing from the root directory.
.. code-block:: console
pytest
has the same practical effects as executing:
.. code-block:: console
pytest testing doc
.. confval:: tmp_path_retention_count .. confval:: tmp_path_retention_count
@ -1744,7 +1752,7 @@ passed multiple times. The expected format is ``name=value``. For example::
[pytest] [pytest]
tmp_path_retention_count = 3 tmp_path_retention_count = 3
Default: 3 Default: ``3``
.. confval:: tmp_path_retention_policy .. confval:: tmp_path_retention_policy
@ -1763,7 +1771,7 @@ passed multiple times. The expected format is ``name=value``. For example::
[pytest] [pytest]
tmp_path_retention_policy = "all" tmp_path_retention_policy = "all"
Default: all Default: ``all``
.. confval:: usefixtures .. confval:: usefixtures

View File

@ -526,7 +526,10 @@ class PytestPluginManager(PluginManager):
# Internal API for local conftest plugin handling. # Internal API for local conftest plugin handling.
# #
def _set_initial_conftests( def _set_initial_conftests(
self, namespace: argparse.Namespace, rootpath: Path self,
namespace: argparse.Namespace,
rootpath: Path,
testpaths_ini: Sequence[str],
) -> None: ) -> None:
"""Load initial conftest files given a preparsed "namespace". """Load initial conftest files given a preparsed "namespace".
@ -543,7 +546,7 @@ class PytestPluginManager(PluginManager):
) )
self._noconftest = namespace.noconftest self._noconftest = namespace.noconftest
self._using_pyargs = namespace.pyargs self._using_pyargs = namespace.pyargs
testpaths = namespace.file_or_dir testpaths = namespace.file_or_dir + testpaths_ini
foundanchor = False foundanchor = False
for testpath in testpaths: for testpath in testpaths:
path = str(testpath) path = str(testpath)
@ -552,7 +555,14 @@ class PytestPluginManager(PluginManager):
if i != -1: if i != -1:
path = path[:i] path = path[:i]
anchor = absolutepath(current / path) anchor = absolutepath(current / path)
if anchor.exists(): # we found some file object
# Ensure we do not break if what appears to be an anchor
# is in fact a very long option (#10169).
try:
anchor_exists = anchor.exists()
except OSError: # pragma: no cover
anchor_exists = False
if anchor_exists:
self._try_load_conftest(anchor, namespace.importmode, rootpath) self._try_load_conftest(anchor, namespace.importmode, rootpath)
foundanchor = True foundanchor = True
if not foundanchor: if not foundanchor:
@ -1131,7 +1141,9 @@ class Config:
@hookimpl(trylast=True) @hookimpl(trylast=True)
def pytest_load_initial_conftests(self, early_config: "Config") -> None: def pytest_load_initial_conftests(self, early_config: "Config") -> None:
self.pluginmanager._set_initial_conftests( self.pluginmanager._set_initial_conftests(
early_config.known_args_namespace, rootpath=early_config.rootpath early_config.known_args_namespace,
rootpath=early_config.rootpath,
testpaths_ini=self.getini("testpaths"),
) )
def _initini(self, args: Sequence[str]) -> None: def _initini(self, args: Sequence[str]) -> None:

View File

@ -1247,6 +1247,48 @@ def test_collect_pyargs_with_testpaths(
result.stdout.fnmatch_lines(["*1 passed in*"]) result.stdout.fnmatch_lines(["*1 passed in*"])
def test_initial_conftests_with_testpaths(pytester: Pytester) -> None:
"""The testpaths ini option should load conftests in those paths as 'initial' (#10987)."""
p = pytester.mkdir("some_path")
p.joinpath("conftest.py").write_text(
textwrap.dedent(
"""
def pytest_sessionstart(session):
raise Exception("pytest_sessionstart hook successfully run")
"""
)
)
pytester.makeini(
"""
[pytest]
testpaths = some_path
"""
)
result = pytester.runpytest()
result.stdout.fnmatch_lines(
"INTERNALERROR* Exception: pytest_sessionstart hook successfully run"
)
def test_large_option_breaks_initial_conftests(pytester: Pytester) -> None:
"""Long option values do not break initial conftests handling (#10169)."""
option_value = "x" * 1024 * 1000
pytester.makeconftest(
"""
def pytest_addoption(parser):
parser.addoption("--xx", default=None)
"""
)
pytester.makepyfile(
f"""
def test_foo(request):
assert request.config.getoption("xx") == {option_value!r}
"""
)
result = pytester.runpytest(f"--xx={option_value}")
assert result.ret == 0
def test_collect_symlink_file_arg(pytester: Pytester) -> None: def test_collect_symlink_file_arg(pytester: Pytester) -> None:
"""Collect a direct symlink works even if it does not match python_files (#4325).""" """Collect a direct symlink works even if it does not match python_files (#4325)."""
real = pytester.makepyfile( real = pytester.makepyfile(

View File

@ -35,7 +35,7 @@ def conftest_setinitial(
self.importmode = "prepend" self.importmode = "prepend"
namespace = cast(argparse.Namespace, Namespace()) namespace = cast(argparse.Namespace, Namespace())
conftest._set_initial_conftests(namespace, rootpath=Path(args[0])) conftest._set_initial_conftests(namespace, rootpath=Path(args[0]), testpaths_ini=[])
@pytest.mark.usefixtures("_sys_snapshot") @pytest.mark.usefixtures("_sys_snapshot")