Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ Guido Wesdorp
Guoqiang Zhang
Hamza Mobeen
Harald Armin Massa
Hariharan Ganesh
Harshna
Henk-Jaap Wagenaar
Holger Kohr
Expand Down
1 change: 1 addition & 0 deletions changelog/9703.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed ``rootdir`` determination when an explicit configuration file is used with test paths outside the configuration directory.
5 changes: 4 additions & 1 deletion doc/en/reference/customize.rst
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,10 @@ Finding the ``rootdir``

Here is the algorithm which finds the rootdir from ``args``:

- If :option:`-c` is passed in the command-line, use that as configuration file, and its directory as ``rootdir``.
- If :option:`-c` is passed in the command-line, use that as configuration file.
When test paths are supplied, its directory participates in ``rootdir``
determination together with those paths; otherwise, its directory becomes the
``rootdir``.

- Determine the common ancestor directory for the specified ``args`` that are
recognised as paths that exist in the file system. If no such paths are
Expand Down
6 changes: 5 additions & 1 deletion src/_pytest/config/findpaths.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,11 @@ def determine_setup(
inipath: Path | None = inipath_
inicfg = load_config_dict_from_file(inipath_) or {}
if rootdir_cmd_arg is None:
rootdir = inipath_.parent
rootdir = (
get_common_ancestor(invocation_dir, [inipath_.parent, *dirs])
if dirs
else inipath_.parent
)
else:
ancestor = get_common_ancestor(invocation_dir, dirs)
rootdir, inipath, inicfg, ignored_config_files = locate_config(
Expand Down
91 changes: 91 additions & 0 deletions testing/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2068,6 +2068,97 @@ def test_explicit_config_file_sets_rootdir(
assert rootpath == tmp_path
assert found_inipath == inipath

def test_explicit_config_file_uses_args_for_rootdir(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
tests_dir = tmp_path / "tests"
tests_dir.mkdir()
config_dir = tmp_path / "config"
config_dir.mkdir()
inipath = config_dir / "pytest.ini"
inipath.touch()

monkeypatch.chdir(tmp_path)

rootpath, found_inipath, *_ = determine_setup(
inifile=str(inipath),
override_ini=None,
args=[str(tests_dir)],
rootdir_cmd_arg=None,
invocation_dir=Path.cwd(),
)
assert rootpath == tmp_path
assert found_inipath == inipath

def test_explicit_config_file_without_args_uses_config_dir(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
config_dir = tmp_path / "config"
config_dir.mkdir()
inipath = config_dir / "pytest.ini"
inipath.touch()

monkeypatch.chdir(tmp_path)

rootpath, found_inipath, *_ = determine_setup(
inifile=str(inipath),
override_ini=None,
args=[],
rootdir_cmd_arg=None,
invocation_dir=Path.cwd(),
)
assert rootpath == config_dir
assert found_inipath == inipath

def test_explicit_config_file_uses_args_for_nodeids(
self, pytester: Pytester
) -> None:
tests_dir = pytester.mkdir("tests")
tests_dir.joinpath("test_file1.py").write_text(
textwrap.dedent(
"""
import pytest

@pytest.fixture(autouse=True)
def some_fixture():
print("Fixture called")

def test_in_file1():
print("test_in_file1")
"""
),
encoding="utf-8",
)
tests_dir.joinpath("test_file2.py").write_text(
textwrap.dedent(
"""
def test_in_file2():
print("test_in_file2")
"""
),
encoding="utf-8",
)
config_dir = pytester.mkdir("config")
config_dir.joinpath("pytest.ini").write_text("[pytest]\n", encoding="utf-8")

result = pytester.runpytest(
"-c",
"config/pytest.ini",
"-s",
"-v",
"tests/test_file1.py",
"tests/test_file2.py",
)

assert result.ret == 0
result.stdout.fnmatch_lines(
[
"tests/test_file1.py::test_in_file1 Fixture called",
"tests/test_file2.py::test_in_file2 test_in_file2",
]
)
result.stdout.no_fnmatch_line("tests/test_file2.py::test_in_file2 Fixture*")

def test_with_arg_outside_cwd_without_inifile(
self, tmp_path: Path, monkeypatch: MonkeyPatch
) -> None:
Expand Down
Loading