2022-11-24 01:46:00 +08:00
|
|
|
import os
|
2020-10-03 23:08:14 +08:00
|
|
|
from pathlib import Path
|
2020-06-08 21:03:10 +08:00
|
|
|
from textwrap import dedent
|
|
|
|
|
|
|
|
import pytest
|
2021-07-06 21:53:32 +08:00
|
|
|
from _pytest.config import UsageError
|
2020-06-08 21:03:10 +08:00
|
|
|
from _pytest.config.findpaths import get_common_ancestor
|
2020-08-13 04:17:43 +08:00
|
|
|
from _pytest.config.findpaths import get_dirs_from_args
|
2022-11-24 01:46:00 +08:00
|
|
|
from _pytest.config.findpaths import is_fs_root
|
2020-06-08 21:03:10 +08:00
|
|
|
from _pytest.config.findpaths import load_config_dict_from_file
|
|
|
|
|
|
|
|
|
|
|
|
class TestLoadConfigDictFromFile:
|
2020-08-03 22:46:35 +08:00
|
|
|
def test_empty_pytest_ini(self, tmp_path: Path) -> None:
|
2020-06-08 21:03:10 +08:00
|
|
|
"""pytest.ini files are always considered for configuration, even if empty"""
|
2020-08-03 22:46:35 +08:00
|
|
|
fn = tmp_path / "pytest.ini"
|
|
|
|
fn.write_text("", encoding="utf-8")
|
2020-06-08 21:03:10 +08:00
|
|
|
assert load_config_dict_from_file(fn) == {}
|
|
|
|
|
2020-08-03 22:46:35 +08:00
|
|
|
def test_pytest_ini(self, tmp_path: Path) -> None:
|
2020-06-08 21:03:10 +08:00
|
|
|
"""[pytest] section in pytest.ini files is read correctly"""
|
2020-08-03 22:46:35 +08:00
|
|
|
fn = tmp_path / "pytest.ini"
|
|
|
|
fn.write_text("[pytest]\nx=1", encoding="utf-8")
|
2020-06-08 21:03:10 +08:00
|
|
|
assert load_config_dict_from_file(fn) == {"x": "1"}
|
|
|
|
|
2020-08-03 22:46:35 +08:00
|
|
|
def test_custom_ini(self, tmp_path: Path) -> None:
|
2020-06-08 21:03:10 +08:00
|
|
|
"""[pytest] section in any .ini file is read correctly"""
|
2020-08-03 22:46:35 +08:00
|
|
|
fn = tmp_path / "custom.ini"
|
|
|
|
fn.write_text("[pytest]\nx=1", encoding="utf-8")
|
2020-06-08 21:03:10 +08:00
|
|
|
assert load_config_dict_from_file(fn) == {"x": "1"}
|
|
|
|
|
2020-08-03 22:46:35 +08:00
|
|
|
def test_custom_ini_without_section(self, tmp_path: Path) -> None:
|
2020-06-08 21:03:10 +08:00
|
|
|
"""Custom .ini files without [pytest] section are not considered for configuration"""
|
2020-08-03 22:46:35 +08:00
|
|
|
fn = tmp_path / "custom.ini"
|
|
|
|
fn.write_text("[custom]", encoding="utf-8")
|
2020-06-08 21:03:10 +08:00
|
|
|
assert load_config_dict_from_file(fn) is None
|
|
|
|
|
2020-08-03 22:46:35 +08:00
|
|
|
def test_custom_cfg_file(self, tmp_path: Path) -> None:
|
2020-06-08 21:03:10 +08:00
|
|
|
"""Custom .cfg files without [tool:pytest] section are not considered for configuration"""
|
2020-08-03 22:46:35 +08:00
|
|
|
fn = tmp_path / "custom.cfg"
|
|
|
|
fn.write_text("[custom]", encoding="utf-8")
|
2020-06-08 21:03:10 +08:00
|
|
|
assert load_config_dict_from_file(fn) is None
|
|
|
|
|
2020-08-03 22:46:35 +08:00
|
|
|
def test_valid_cfg_file(self, tmp_path: Path) -> None:
|
2020-06-08 21:03:10 +08:00
|
|
|
"""Custom .cfg files with [tool:pytest] section are read correctly"""
|
2020-08-03 22:46:35 +08:00
|
|
|
fn = tmp_path / "custom.cfg"
|
|
|
|
fn.write_text("[tool:pytest]\nx=1", encoding="utf-8")
|
2020-06-08 21:03:10 +08:00
|
|
|
assert load_config_dict_from_file(fn) == {"x": "1"}
|
|
|
|
|
2020-08-03 22:46:35 +08:00
|
|
|
def test_unsupported_pytest_section_in_cfg_file(self, tmp_path: Path) -> None:
|
2020-06-08 21:03:10 +08:00
|
|
|
""".cfg files with [pytest] section are no longer supported and should fail to alert users"""
|
2020-08-03 22:46:35 +08:00
|
|
|
fn = tmp_path / "custom.cfg"
|
|
|
|
fn.write_text("[pytest]", encoding="utf-8")
|
2020-06-08 21:03:10 +08:00
|
|
|
with pytest.raises(pytest.fail.Exception):
|
|
|
|
load_config_dict_from_file(fn)
|
|
|
|
|
2020-08-03 22:46:35 +08:00
|
|
|
def test_invalid_toml_file(self, tmp_path: Path) -> None:
|
2021-07-06 21:53:32 +08:00
|
|
|
"""Invalid .toml files should raise `UsageError`."""
|
|
|
|
fn = tmp_path / "myconfig.toml"
|
|
|
|
fn.write_text("]invalid toml[", encoding="utf-8")
|
|
|
|
with pytest.raises(UsageError):
|
|
|
|
load_config_dict_from_file(fn)
|
|
|
|
|
|
|
|
def test_custom_toml_file(self, tmp_path: Path) -> None:
|
2020-06-08 21:03:10 +08:00
|
|
|
""".toml files without [tool.pytest.ini_options] are not considered for configuration."""
|
2020-08-03 22:46:35 +08:00
|
|
|
fn = tmp_path / "myconfig.toml"
|
|
|
|
fn.write_text(
|
2020-06-08 21:03:10 +08:00
|
|
|
dedent(
|
|
|
|
"""
|
|
|
|
[build_system]
|
|
|
|
x = 1
|
|
|
|
"""
|
2020-08-03 22:46:35 +08:00
|
|
|
),
|
|
|
|
encoding="utf-8",
|
2020-06-08 21:03:10 +08:00
|
|
|
)
|
|
|
|
assert load_config_dict_from_file(fn) is None
|
|
|
|
|
2020-08-03 22:46:35 +08:00
|
|
|
def test_valid_toml_file(self, tmp_path: Path) -> None:
|
2020-06-08 21:03:10 +08:00
|
|
|
""".toml files with [tool.pytest.ini_options] are read correctly, including changing
|
|
|
|
data types to str/list for compatibility with other configuration options."""
|
2020-08-03 22:46:35 +08:00
|
|
|
fn = tmp_path / "myconfig.toml"
|
|
|
|
fn.write_text(
|
2020-06-08 21:03:10 +08:00
|
|
|
dedent(
|
|
|
|
"""
|
|
|
|
[tool.pytest.ini_options]
|
|
|
|
x = 1
|
|
|
|
y = 20.0
|
|
|
|
values = ["tests", "integration"]
|
|
|
|
name = "foo"
|
2021-07-06 05:26:01 +08:00
|
|
|
heterogeneous_array = [1, "str"]
|
2020-06-08 21:03:10 +08:00
|
|
|
"""
|
2020-08-03 22:46:35 +08:00
|
|
|
),
|
|
|
|
encoding="utf-8",
|
2020-06-08 21:03:10 +08:00
|
|
|
)
|
|
|
|
assert load_config_dict_from_file(fn) == {
|
|
|
|
"x": "1",
|
|
|
|
"y": "20.0",
|
|
|
|
"values": ["tests", "integration"],
|
|
|
|
"name": "foo",
|
2021-07-06 05:26:01 +08:00
|
|
|
"heterogeneous_array": [1, "str"],
|
2020-06-08 21:03:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class TestCommonAncestor:
|
2020-08-03 22:46:35 +08:00
|
|
|
def test_has_ancestor(self, tmp_path: Path) -> None:
|
|
|
|
fn1 = tmp_path / "foo" / "bar" / "test_1.py"
|
|
|
|
fn1.parent.mkdir(parents=True)
|
|
|
|
fn1.touch()
|
|
|
|
fn2 = tmp_path / "foo" / "zaz" / "test_2.py"
|
|
|
|
fn2.parent.mkdir(parents=True)
|
|
|
|
fn2.touch()
|
2024-01-17 02:56:32 +08:00
|
|
|
cwd = Path.cwd()
|
|
|
|
assert get_common_ancestor(cwd, [fn1, fn2]) == tmp_path / "foo"
|
|
|
|
assert get_common_ancestor(cwd, [fn1.parent, fn2]) == tmp_path / "foo"
|
|
|
|
assert get_common_ancestor(cwd, [fn1.parent, fn2.parent]) == tmp_path / "foo"
|
|
|
|
assert get_common_ancestor(cwd, [fn1, fn2.parent]) == tmp_path / "foo"
|
2020-08-03 22:46:35 +08:00
|
|
|
|
|
|
|
def test_single_dir(self, tmp_path: Path) -> None:
|
2024-01-17 02:56:32 +08:00
|
|
|
assert get_common_ancestor(Path.cwd(), [tmp_path]) == tmp_path
|
2020-08-03 22:46:35 +08:00
|
|
|
|
|
|
|
def test_single_file(self, tmp_path: Path) -> None:
|
|
|
|
fn = tmp_path / "foo.py"
|
|
|
|
fn.touch()
|
2024-01-17 02:56:32 +08:00
|
|
|
assert get_common_ancestor(Path.cwd(), [fn]) == tmp_path
|
2020-08-13 04:17:43 +08:00
|
|
|
|
|
|
|
|
|
|
|
def test_get_dirs_from_args(tmp_path):
|
|
|
|
"""get_dirs_from_args() skips over non-existing directories and files"""
|
|
|
|
fn = tmp_path / "foo.py"
|
|
|
|
fn.touch()
|
|
|
|
d = tmp_path / "tests"
|
|
|
|
d.mkdir()
|
|
|
|
option = "--foobar=/foo.txt"
|
|
|
|
# xdist uses options in this format for its rsync feature (#7638)
|
|
|
|
xdist_rsync_option = "popen=c:/dest"
|
|
|
|
assert get_dirs_from_args(
|
|
|
|
[str(fn), str(tmp_path / "does_not_exist"), str(d), option, xdist_rsync_option]
|
|
|
|
) == [fn.parent, d]
|
2022-11-24 01:46:00 +08:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"path, expected",
|
|
|
|
[
|
|
|
|
pytest.param(
|
|
|
|
f"e:{os.sep}", True, marks=pytest.mark.skipif("sys.platform != 'win32'")
|
|
|
|
),
|
|
|
|
(f"{os.sep}", True),
|
|
|
|
(f"e:{os.sep}projects", False),
|
|
|
|
(f"{os.sep}projects", False),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_is_fs_root(path: Path, expected: bool) -> None:
|
|
|
|
assert is_fs_root(Path(path)) is expected
|