diff --git a/tests/conftest.py b/tests/conftest.py index db9eda95..a560816e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,6 +8,7 @@ import subprocess import sys import sysconfig +from collections.abc import Iterable from importlib import metadata from pathlib import Path from typing import Any, Literal, overload @@ -19,6 +20,11 @@ else: import tomllib +if sys.version_info < (3, 10): + from typing_extensions import TypeGuard +else: + from typing import TypeGuard + import pytest from packaging.requirements import Requirement @@ -153,6 +159,21 @@ def install(self, *args: str, isolated: bool = True) -> None: isolated_flags = "" if isolated else ["--no-build-isolation"] self.module("pip", "install", *isolated_flags, *args) + def prepare_no_build_isolation(self) -> None: + if not self.wheelhouse: + msg = "Wheelhouse was not setup." + raise ValueError(msg) + + ninja = [ + "ninja" for f in self.wheelhouse.iterdir() if f.name.startswith("ninja-") + ] + cmake = [ + "cmake" for f in self.wheelhouse.iterdir() if f.name.startswith("cmake-") + ] + + self.install("pip>23") + self.install("scikit-build-core", *ninja, *cmake) + @pytest.fixture def isolated(tmp_path: Path, pep518_wheelhouse: Path) -> VEnv: @@ -169,6 +190,7 @@ def virtualenv(tmp_path: Path) -> VEnv: @dataclasses.dataclass(frozen=True) class PackageInfo: name: str + workdir: Path sdist_hash38: str | None = None sdist_hash39: str | None = None sdist_dated_hash39: str | None = None @@ -192,158 +214,114 @@ def source_date_epoch(self) -> str: def process_package( - package: PackageInfo, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + package: PackageInfo, + monkeypatch: pytest.MonkeyPatch, ) -> None: - package_dir = tmp_path / "pkg" - shutil.copytree(DIR / "packages" / package.name, package_dir) - monkeypatch.chdir(package_dir) - - -@pytest.fixture -def package_simple_pyproject_ext( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> PackageInfo: - package = PackageInfo( - "simple_pyproject_ext", - "71b4e95854ef8d04886758d24d18fe55ebe63648310acf58c7423387cca73508", - "ed930179fbf5adc2e71a64a6f9686c61fdcce477c85bc94dd51598641be886a7", - "0178462b64b4eb9c41ae70eb413a9cc111c340e431b240af1b218fe81b0c2ecb", - "de79895a9d5c2112257715214ab419d3635e841716655e8a55390e5d52445819", - ) - process_package(package, tmp_path, monkeypatch) - return package - - -@pytest.fixture -def package_simple_pyproject_script_with_flags( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> PackageInfo: - package = PackageInfo( - "simple_pyproject_script_with_flags", - ) - process_package(package, tmp_path, monkeypatch) - return package - - -@pytest.fixture -def package_simple_pyproject_source_dir( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> PackageInfo: - package = PackageInfo( - "simple_pyproject_source_dir", - ) - process_package(package, tmp_path, monkeypatch) - return package - - -@pytest.fixture -def package_simple_setuptools_ext( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> PackageInfo: - package = PackageInfo("simple_setuptools_ext") - process_package(package, tmp_path, monkeypatch) - return package - - -@pytest.fixture -def package_toml_setuptools_ext( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> PackageInfo: - package = PackageInfo("toml_setuptools_ext") - process_package(package, tmp_path, monkeypatch) - return package + pkg_src = DIR / "packages" / package.name + assert pkg_src.exists() + shutil.copytree(pkg_src, package.workdir, dirs_exist_ok=True) + monkeypatch.chdir(package.workdir) @pytest.fixture -def package_mixed_setuptools( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch +def package( + request: pytest.FixtureRequest, + tmp_path_factory: pytest.TempPathFactory, + monkeypatch: pytest.MonkeyPatch, ) -> PackageInfo: - package = PackageInfo("mixed_setuptools") - process_package(package, tmp_path, monkeypatch) + pkg_name = request.param + assert isinstance(pkg_name, str) + package = PackageInfo(pkg_name, tmp_path_factory.mktemp("pkg")) + assert (DIR / "packages" / package.name).exists() + process_package(package, monkeypatch) return package @pytest.fixture -def package_filepath_pure( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> PackageInfo: - package = PackageInfo("filepath_pure") - process_package(package, tmp_path, monkeypatch) - return package +def multiple_packages( + request: pytest.FixtureRequest, + tmp_path_factory: pytest.TempPathFactory, + monkeypatch: pytest.MonkeyPatch, +) -> list[PackageInfo]: + package_names = request.param + assert isinstance(package_names, Iterable) + packages = [] + for pkg_name in package_names: + pkg = PackageInfo(pkg_name, tmp_path_factory.mktemp("pkg")) + process_package(pkg, monkeypatch) + packages.append(pkg) + monkeypatch.chdir(tmp_path_factory.getbasetemp()) + return packages @pytest.fixture -def package_dynamic_metadata( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch +def package_simple_pyproject_ext( + tmp_path_factory: pytest.TempPathFactory, + monkeypatch: pytest.MonkeyPatch, ) -> PackageInfo: - package = PackageInfo("dynamic_metadata") - process_package(package, tmp_path, monkeypatch) - return package - - -@pytest.fixture -def package_hatchling(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> PackageInfo: - package = PackageInfo("hatchling") - process_package(package, tmp_path, monkeypatch) - return package - - -@pytest.fixture -def package_simplest_c(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> PackageInfo: package = PackageInfo( - "simplest_c", - ) - process_package(package, tmp_path, monkeypatch) - return package - - -@pytest.fixture -def navigate_editable(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> PackageInfo: - package = PackageInfo( - "navigate_editable", + "simple_pyproject_ext", + tmp_path_factory.mktemp("pkg"), + "71b4e95854ef8d04886758d24d18fe55ebe63648310acf58c7423387cca73508", + "ed930179fbf5adc2e71a64a6f9686c61fdcce477c85bc94dd51598641be886a7", + "0178462b64b4eb9c41ae70eb413a9cc111c340e431b240af1b218fe81b0c2ecb", + "de79895a9d5c2112257715214ab419d3635e841716655e8a55390e5d52445819", ) - process_package(package, tmp_path, monkeypatch) + process_package(package, monkeypatch) return package -@pytest.fixture -def broken_fallback(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> PackageInfo: - package = PackageInfo( - "broken_fallback", +@dataclasses.dataclass(frozen=True) +class Isolate: + state: bool + flags: list[str] + + +@pytest.fixture(params=[True, False], ids=["isolated", "not_isolated"]) +def isolate(request: pytest.FixtureRequest, isolated: VEnv) -> Isolate: + isolate_request = request.param + assert isinstance(isolate_request, bool) + if not isolate_request: + isolated.prepare_no_build_isolation() + flags = [] + if not isolate_request: + flags.append("--no-build-isolation") + return Isolate( + state=isolate_request, + flags=flags, ) - process_package(package, tmp_path, monkeypatch) - return package -@pytest.fixture -def package_sdist_config( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> PackageInfo: - package = PackageInfo( - "sdist_config", - ) - process_package(package, tmp_path, monkeypatch) - return package +def is_editable_mode(maybe_mode: str) -> TypeGuard[Literal["redirect", "inplace"]]: + return maybe_mode in {"redirect", "inplace"} -@pytest.fixture -def package_simple_purelib_package( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> PackageInfo: - package = PackageInfo( - "simple_purelib_package", - ) - process_package(package, tmp_path, monkeypatch) - return package - +@dataclasses.dataclass(frozen=True) +class Editable: + mode: Literal["redirect", "inplace"] | None + config_settings: list[str] -@pytest.fixture -def package_pep639_pure(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> PackageInfo: - package = PackageInfo( - "pep639_pure", + @property + def flags(self) -> list[str]: + if not self.mode: + return self.config_settings + return [*self.config_settings, "-e"] + + +@pytest.fixture(params=[pytest.param(None, id="not_editable"), "redirect", "inplace"]) +def editable(request: pytest.FixtureRequest) -> Editable: + editable_mode = request.param + assert editable_mode is None or is_editable_mode(editable_mode) + config_settings = [] + if editable_mode: + config_settings.append(f"--config-settings=editable.mode={editable_mode}") + if editable_mode != "inplace": + build_dir = "build/{wheel_tag}" + config_settings.append(f"--config-settings=build-dir={build_dir}") + return Editable( + mode=editable_mode, + config_settings=config_settings, ) - process_package(package, tmp_path, monkeypatch) - return package def which_mock(name: str) -> str | None: diff --git a/tests/packages/importlib_editable/pyproject.toml b/tests/packages/importlib_editable/pyproject.toml index dae7eb5a..b5884f93 100644 --- a/tests/packages/importlib_editable/pyproject.toml +++ b/tests/packages/importlib_editable/pyproject.toml @@ -7,4 +7,5 @@ name = "pkg" version = "0.0.1" [tool.scikit-build] +cmake.source-dir = "src" build-dir = "build/{wheel_tag}" diff --git a/tests/packages/importlib_editable/CMakeLists.txt b/tests/packages/importlib_editable/src/CMakeLists.txt similarity index 100% rename from tests/packages/importlib_editable/CMakeLists.txt rename to tests/packages/importlib_editable/src/CMakeLists.txt diff --git a/tests/packages/importlib_editable/emod.c b/tests/packages/importlib_editable/src/emod.c similarity index 100% rename from tests/packages/importlib_editable/emod.c rename to tests/packages/importlib_editable/src/emod.c diff --git a/tests/packages/importlib_editable/emod.pyi b/tests/packages/importlib_editable/src/emod.pyi similarity index 100% rename from tests/packages/importlib_editable/emod.pyi rename to tests/packages/importlib_editable/src/emod.pyi diff --git a/tests/packages/importlib_editable/pkg/CMakeLists.txt b/tests/packages/importlib_editable/src/pkg/CMakeLists.txt similarity index 100% rename from tests/packages/importlib_editable/pkg/CMakeLists.txt rename to tests/packages/importlib_editable/src/pkg/CMakeLists.txt diff --git a/tests/packages/importlib_editable/pkg/__init__.py b/tests/packages/importlib_editable/src/pkg/__init__.py similarity index 100% rename from tests/packages/importlib_editable/pkg/__init__.py rename to tests/packages/importlib_editable/src/pkg/__init__.py diff --git a/tests/packages/importlib_editable/pkg/emod_a.c b/tests/packages/importlib_editable/src/pkg/emod_a.c similarity index 100% rename from tests/packages/importlib_editable/pkg/emod_a.c rename to tests/packages/importlib_editable/src/pkg/emod_a.c diff --git a/tests/packages/importlib_editable/pkg/emod_a.pyi b/tests/packages/importlib_editable/src/pkg/emod_a.pyi similarity index 100% rename from tests/packages/importlib_editable/pkg/emod_a.pyi rename to tests/packages/importlib_editable/src/pkg/emod_a.pyi diff --git a/tests/packages/importlib_editable/pkg/pmod_a.py b/tests/packages/importlib_editable/src/pkg/pmod_a.py similarity index 100% rename from tests/packages/importlib_editable/pkg/pmod_a.py rename to tests/packages/importlib_editable/src/pkg/pmod_a.py diff --git a/tests/packages/importlib_editable/pkg/sub_a/CMakeLists.txt b/tests/packages/importlib_editable/src/pkg/sub_a/CMakeLists.txt similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_a/CMakeLists.txt rename to tests/packages/importlib_editable/src/pkg/sub_a/CMakeLists.txt diff --git a/tests/packages/importlib_editable/pkg/sub_a/__init__.py b/tests/packages/importlib_editable/src/pkg/sub_a/__init__.py similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_a/__init__.py rename to tests/packages/importlib_editable/src/pkg/sub_a/__init__.py diff --git a/tests/packages/importlib_editable/pkg/sub_a/emod_b.c b/tests/packages/importlib_editable/src/pkg/sub_a/emod_b.c similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_a/emod_b.c rename to tests/packages/importlib_editable/src/pkg/sub_a/emod_b.c diff --git a/tests/packages/importlib_editable/pkg/sub_a/emod_b.pyi b/tests/packages/importlib_editable/src/pkg/sub_a/emod_b.pyi similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_a/emod_b.pyi rename to tests/packages/importlib_editable/src/pkg/sub_a/emod_b.pyi diff --git a/tests/packages/importlib_editable/pkg/sub_a/pmod_b.py b/tests/packages/importlib_editable/src/pkg/sub_a/pmod_b.py similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_a/pmod_b.py rename to tests/packages/importlib_editable/src/pkg/sub_a/pmod_b.py diff --git a/tests/packages/importlib_editable/pkg/sub_b/CMakeLists.txt b/tests/packages/importlib_editable/src/pkg/sub_b/CMakeLists.txt similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_b/CMakeLists.txt rename to tests/packages/importlib_editable/src/pkg/sub_b/CMakeLists.txt diff --git a/tests/packages/importlib_editable/pkg/sub_b/__init__.py b/tests/packages/importlib_editable/src/pkg/sub_b/__init__.py similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_b/__init__.py rename to tests/packages/importlib_editable/src/pkg/sub_b/__init__.py diff --git a/tests/packages/importlib_editable/pkg/sub_b/emod_c.c b/tests/packages/importlib_editable/src/pkg/sub_b/emod_c.c similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_b/emod_c.c rename to tests/packages/importlib_editable/src/pkg/sub_b/emod_c.c diff --git a/tests/packages/importlib_editable/pkg/sub_b/emod_c.pyi b/tests/packages/importlib_editable/src/pkg/sub_b/emod_c.pyi similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_b/emod_c.pyi rename to tests/packages/importlib_editable/src/pkg/sub_b/emod_c.pyi diff --git a/tests/packages/importlib_editable/pkg/sub_b/pmod_c.py b/tests/packages/importlib_editable/src/pkg/sub_b/pmod_c.py similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_b/pmod_c.py rename to tests/packages/importlib_editable/src/pkg/sub_b/pmod_c.py diff --git a/tests/packages/importlib_editable/pkg/sub_b/sub_c/CMakeLists.txt b/tests/packages/importlib_editable/src/pkg/sub_b/sub_c/CMakeLists.txt similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_b/sub_c/CMakeLists.txt rename to tests/packages/importlib_editable/src/pkg/sub_b/sub_c/CMakeLists.txt diff --git a/tests/packages/importlib_editable/pkg/sub_b/sub_c/__init__.py b/tests/packages/importlib_editable/src/pkg/sub_b/sub_c/__init__.py similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_b/sub_c/__init__.py rename to tests/packages/importlib_editable/src/pkg/sub_b/sub_c/__init__.py diff --git a/tests/packages/importlib_editable/pkg/sub_b/sub_c/emod_d.c b/tests/packages/importlib_editable/src/pkg/sub_b/sub_c/emod_d.c similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_b/sub_c/emod_d.c rename to tests/packages/importlib_editable/src/pkg/sub_b/sub_c/emod_d.c diff --git a/tests/packages/importlib_editable/pkg/sub_b/sub_c/emod_d.pyi b/tests/packages/importlib_editable/src/pkg/sub_b/sub_c/emod_d.pyi similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_b/sub_c/emod_d.pyi rename to tests/packages/importlib_editable/src/pkg/sub_b/sub_c/emod_d.pyi diff --git a/tests/packages/importlib_editable/pkg/sub_b/sub_c/pmod_d.py b/tests/packages/importlib_editable/src/pkg/sub_b/sub_c/pmod_d.py similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_b/sub_c/pmod_d.py rename to tests/packages/importlib_editable/src/pkg/sub_b/sub_c/pmod_d.py diff --git a/tests/packages/importlib_editable/pkg/sub_b/sub_d/CMakeLists.txt b/tests/packages/importlib_editable/src/pkg/sub_b/sub_d/CMakeLists.txt similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_b/sub_d/CMakeLists.txt rename to tests/packages/importlib_editable/src/pkg/sub_b/sub_d/CMakeLists.txt diff --git a/tests/packages/importlib_editable/pkg/sub_b/sub_d/__init__.py b/tests/packages/importlib_editable/src/pkg/sub_b/sub_d/__init__.py similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_b/sub_d/__init__.py rename to tests/packages/importlib_editable/src/pkg/sub_b/sub_d/__init__.py diff --git a/tests/packages/importlib_editable/pkg/sub_b/sub_d/emod_e.c b/tests/packages/importlib_editable/src/pkg/sub_b/sub_d/emod_e.c similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_b/sub_d/emod_e.c rename to tests/packages/importlib_editable/src/pkg/sub_b/sub_d/emod_e.c diff --git a/tests/packages/importlib_editable/pkg/sub_b/sub_d/emod_e.pyi b/tests/packages/importlib_editable/src/pkg/sub_b/sub_d/emod_e.pyi similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_b/sub_d/emod_e.pyi rename to tests/packages/importlib_editable/src/pkg/sub_b/sub_d/emod_e.pyi diff --git a/tests/packages/importlib_editable/pkg/sub_b/sub_d/pmod_e.py b/tests/packages/importlib_editable/src/pkg/sub_b/sub_d/pmod_e.py similarity index 100% rename from tests/packages/importlib_editable/pkg/sub_b/sub_d/pmod_e.py rename to tests/packages/importlib_editable/src/pkg/sub_b/sub_d/pmod_e.py diff --git a/tests/packages/importlib_editable/pmod.py b/tests/packages/importlib_editable/src/pmod.py similarity index 100% rename from tests/packages/importlib_editable/pmod.py rename to tests/packages/importlib_editable/src/pmod.py diff --git a/tests/test_broken_fallback.py b/tests/test_broken_fallback.py index 3730037c..c7cebeb7 100644 --- a/tests/test_broken_fallback.py +++ b/tests/test_broken_fallback.py @@ -11,7 +11,8 @@ @pytest.mark.compile @pytest.mark.configure -@pytest.mark.usefixtures("broken_fallback") +@pytest.mark.parametrize("package", {"broken_fallback"}, indirect=True) +@pytest.mark.usefixtures("package") @pytest.mark.parametrize("broken_define", ["BROKEN_CMAKE", "BROKEN_CODE"]) def test_broken_code( broken_define: str, capfd: pytest.CaptureFixture[str], tmp_path: Path @@ -39,7 +40,8 @@ def test_broken_code( assert "CMake build failed" in out -@pytest.mark.usefixtures("broken_fallback") +@pytest.mark.parametrize("package", {"broken_fallback"}, indirect=True) +@pytest.mark.usefixtures("package") def test_fail_setting( monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ): @@ -54,7 +56,8 @@ def test_fail_setting( assert "fail setting was enabled" in err -@pytest.mark.usefixtures("broken_fallback") +@pytest.mark.parametrize("package", {"broken_fallback"}, indirect=True) +@pytest.mark.usefixtures("package") def test_fail_setting_msg( monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ): diff --git a/tests/test_dynamic_metadata.py b/tests/test_dynamic_metadata.py index 9038edc5..60fdfd0f 100644 --- a/tests/test_dynamic_metadata.py +++ b/tests/test_dynamic_metadata.py @@ -99,7 +99,8 @@ def mock_entry_points(monkeypatch): monkeypatch.setattr(importlib, "import_module", special_loader) -@pytest.mark.usefixtures("mock_entry_points", "package_dynamic_metadata") +@pytest.mark.parametrize("package", {"dynamic_metadata"}, indirect=True) +@pytest.mark.usefixtures("mock_entry_points", "package") def test_dynamic_metadata(): with Path("pyproject.toml").open("rb") as ft: pyproject = tomllib.load(ft) @@ -115,7 +116,8 @@ def test_dynamic_metadata(): assert metadata.readme == pyproject_metadata.Readme("Some text", None, "text/x-rst") -@pytest.mark.usefixtures("package_dynamic_metadata") +@pytest.mark.parametrize("package", {"dynamic_metadata"}, indirect=True) +@pytest.mark.usefixtures("package") def test_plugin_metadata(): reason_msg = ( "install hatch-fancy-pypi-readme and setuptools-scm to test the " @@ -157,7 +159,8 @@ def test_plugin_metadata(): assert metadata.optional_dependencies == {"dev": [Requirement("fancy==0.1.0")]} -@pytest.mark.usefixtures("package_dynamic_metadata") +@pytest.mark.parametrize("package", {"dynamic_metadata"}, indirect=True) +@pytest.mark.usefixtures("package") def test_faulty_metadata(): reason_msg = "install hatch-fancy-pypi-readme to test the dynamic metadata plugins" pytest.importorskip("hatch_fancy_pypi_readme", reason=reason_msg) @@ -173,7 +176,8 @@ def test_faulty_metadata(): get_standard_metadata(pyproject, settings) -@pytest.mark.usefixtures("package_dynamic_metadata") +@pytest.mark.parametrize("package", {"dynamic_metadata"}, indirect=True) +@pytest.mark.usefixtures("package") def test_local_plugin_metadata(): with Path("local_pyproject.toml").open("rb") as ft: pyproject = tomllib.load(ft) @@ -186,7 +190,8 @@ def test_local_plugin_metadata(): assert metadata.version == Version("3.2.1") -@pytest.mark.usefixtures("package_dynamic_metadata") +@pytest.mark.parametrize("package", {"dynamic_metadata"}, indirect=True) +@pytest.mark.usefixtures("package") def test_warn_metadata(): with Path("warn_project.toml").open("rb") as ft: pyproject = tomllib.load(ft) @@ -199,7 +204,8 @@ def test_warn_metadata(): get_standard_metadata(pyproject, settings) -@pytest.mark.usefixtures("package_dynamic_metadata") +@pytest.mark.parametrize("package", {"dynamic_metadata"}, indirect=True) +@pytest.mark.usefixtures("package") def test_fail_experimental_metadata(): with Path("warn_project.toml").open("rb") as ft: pyproject = tomllib.load(ft) @@ -213,7 +219,8 @@ def test_fail_experimental_metadata(): assert value == 7 -@pytest.mark.usefixtures("mock_entry_points", "package_dynamic_metadata") +@pytest.mark.parametrize("package", {"dynamic_metadata"}, indirect=True) +@pytest.mark.usefixtures("mock_entry_points", "package") def test_dual_metadata(): with Path("dual_project.toml").open("rb") as ft: pyproject = tomllib.load(ft) @@ -239,7 +246,8 @@ def test_dual_metadata(): @pytest.mark.compile @pytest.mark.configure -@pytest.mark.usefixtures("mock_entry_points", "package_dynamic_metadata") +@pytest.mark.parametrize("package", {"dynamic_metadata"}, indirect=True) +@pytest.mark.usefixtures("mock_entry_points", "package") def test_pep517_wheel(virtualenv, tmp_path: Path) -> None: dist = tmp_path / "dist" out = build_wheel(str(dist)) @@ -354,8 +362,9 @@ def test_regex_remove( assert version == ("1.2.3dev1" if dev else "1.2.3") -@pytest.mark.usefixtures("package_dynamic_metadata") @pytest.mark.parametrize("override", [None, "env", "sdist"]) +@pytest.mark.parametrize("package", {"dynamic_metadata"}, indirect=True) +@pytest.mark.usefixtures("package") def test_build_requires_field(override, monkeypatch) -> None: shutil.copy("build_requires_project.toml", "pyproject.toml") diff --git a/tests/test_editable.py b/tests/test_editable.py index 6d4a1c18..04249def 100644 --- a/tests/test_editable.py +++ b/tests/test_editable.py @@ -6,15 +6,13 @@ from pathlib import Path import pytest -from conftest import PackageInfo, VEnv, process_package @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration -@pytest.mark.parametrize("isolate", [True, False], ids=["isolated", "notisolated"]) @pytest.mark.parametrize( - "package", + "py_pkg", [ pytest.param( True, @@ -24,22 +22,18 @@ pytest.param(False, id="datafolder"), ], ) -@pytest.mark.usefixtures("navigate_editable") +@pytest.mark.parametrize("package", {"navigate_editable"}, indirect=True) +@pytest.mark.usefixtures("package") @pytest.mark.xfail( sys.version_info[:2] == (3, 9), reason="Python 3.9 not supported yet" ) -def test_navigate_editable(isolated, isolate, package): - isolate_args = ["--no-build-isolation"] if not isolate else [] - isolated.install("pip>=23") - if not isolate: - isolated.install("scikit-build-core") - - if package: +def test_navigate_editable(isolated, isolate, py_pkg): + if py_pkg: init_py = Path("python/shared_pkg/data/__init__.py") init_py.touch() isolated.install( - "-v", "--config-settings=build-dir=build/{wheel_tag}", *isolate_args, "-e", "." + "-v", "--config-settings=build-dir=build/{wheel_tag}", *isolate.flags, "-e", "." ) value = isolated.execute("import shared_pkg; shared_pkg.call_c_method()") @@ -58,67 +52,32 @@ def test_navigate_editable(isolated, isolate, package): @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration +@pytest.mark.parametrize("isolate", {False}, indirect=True) @pytest.mark.parametrize( - ("editable", "editable_mode"), [(False, ""), (True, "redirect"), (True, "inplace")] + "multiple_packages", + [["cython_pxd_editable/pkg1", "cython_pxd_editable/pkg2"]], + indirect=True, ) -def test_cython_pxd(monkeypatch, tmp_path, editable, editable_mode, isolated): - editable_flag = ["-e"] if editable else [] - - config_mode_flags = [] - if editable: - config_mode_flags.append(f"--config-settings=editable.mode={editable_mode}") - if editable_mode != "inplace": - config_mode_flags.append("--config-settings=build-dir=build/{wheel_tag}") - - package1 = PackageInfo( - "cython_pxd_editable/pkg1", - ) - tmp_path1 = tmp_path / "pkg1" - tmp_path1.mkdir() - process_package(package1, tmp_path1, monkeypatch) - - ninja = [ - "ninja" for f in isolated.wheelhouse.iterdir() if f.name.startswith("ninja-") - ] - cmake = [ - "cmake" for f in isolated.wheelhouse.iterdir() if f.name.startswith("cmake-") - ] - - isolated.install("pip>23") - isolated.install("cython", "scikit-build-core", *ninja, *cmake) - - isolated.install( - "-v", - *config_mode_flags, - "--no-build-isolation", - *editable_flag, - ".", - ) - - package2 = PackageInfo( - "cython_pxd_editable/pkg2", - ) - tmp_path2 = tmp_path / "pkg2" - tmp_path2.mkdir() - process_package(package2, tmp_path2, monkeypatch) +def test_cython_pxd(multiple_packages, editable, isolated, isolate): + isolated.install("cython") - isolated.install( - "-v", - *config_mode_flags, - "--no-build-isolation", - *editable_flag, - ".", - ) + # install the packages in order with one dependent on the other + for package in multiple_packages: + isolated.install( + "-v", + *isolate.flags, + *editable.flags, + str(package.workdir), + ) @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration -@pytest.mark.usefixtures("package_simplest_c") -def test_install_dir(isolated): - isolated.install("pip>=23") - isolated.install("scikit-build-core") - +@pytest.mark.parametrize("package", {"simplest_c"}, indirect=True) +@pytest.mark.parametrize("isolate", {False}, indirect=True) +@pytest.mark.usefixtures("package") +def test_install_dir(isolated, isolate): settings_overrides = { "build-dir": "build/{wheel_tag}", "wheel.install-dir": "other_pkg", @@ -139,7 +98,7 @@ def test_install_dir(isolated): isolated.install( "-v", *[f"--config-settings={k}={v}" for k, v in settings_overrides.items()], - "--no-build-isolation", + *isolate.flags, "-e", ".", ) @@ -161,67 +120,22 @@ def test_install_dir(isolated): assert not failed_c_module.exists() -def _setup_package_for_editable_layout_tests( - monkeypatch: pytest.MonkeyPatch, - tmp_path: Path, - editable: bool, - editable_mode: str, - isolated: VEnv, -) -> None: - editable_flag = ["-e"] if editable else [] - - config_mode_flags = [] - if editable: - config_mode_flags.append(f"--config-settings=editable.mode={editable_mode}") - if editable_mode != "inplace": - config_mode_flags.append("--config-settings=build-dir=build/{wheel_tag}") - - # Use a context so that we only change into the directory up until the point where - # we run the editable install. We do not want to be in that directory when importing - # to avoid importing the source directory instead of the installed package. - with monkeypatch.context() as m: - package = PackageInfo("importlib_editable") - process_package(package, tmp_path, m) - - assert isolated.wheelhouse - - ninja = [ - "ninja" - for f in isolated.wheelhouse.iterdir() - if f.name.startswith("ninja-") - ] - cmake = [ - "cmake" - for f in isolated.wheelhouse.iterdir() - if f.name.startswith("cmake-") - ] - - isolated.install("pip>23") - isolated.install("scikit-build-core", *ninja, *cmake) - - isolated.install( - "-v", - *config_mode_flags, - "--no-build-isolation", - *editable_flag, - ".", - ) - - @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration -@pytest.mark.parametrize( - ("editable", "editable_mode"), [(False, ""), (True, "redirect"), (True, "inplace")] -) -def test_direct_import(monkeypatch, tmp_path, editable, editable_mode, isolated): +@pytest.mark.parametrize("package", {"importlib_editable"}, indirect=True) +@pytest.mark.usefixtures("package") +def test_direct_import(editable, isolated): # TODO: Investigate these failures - if platform.system() == "Windows" and editable_mode == "inplace": + if platform.system() == "Windows" and editable.mode == "inplace": pytest.xfail("Windows fails to import the top-level extension module") - _setup_package_for_editable_layout_tests( - monkeypatch, tmp_path, editable, editable_mode, isolated + isolated.install( + "-v", + *editable.flags, + ".", ) + isolated.execute("import pkg") isolated.execute("import pmod") isolated.execute("import emod") @@ -230,30 +144,22 @@ def test_direct_import(monkeypatch, tmp_path, editable, editable_mode, isolated) @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration -@pytest.mark.parametrize( - ("editable", "editable_mode"), - [ - (False, ""), - pytest.param( - True, - "redirect", - marks=pytest.mark.xfail, - ), - (True, "inplace"), - ], -) -def test_importlib_resources(monkeypatch, tmp_path, editable, editable_mode, isolated): +@pytest.mark.parametrize("package", {"importlib_editable"}, indirect=True) +@pytest.mark.usefixtures("package") +def test_importlib_resources(editable, isolated): if sys.version_info < (3, 9): pytest.skip("importlib.resources.files is introduced in Python 3.9") # TODO: Investigate these failures - if editable_mode == "redirect": + if editable.mode == "redirect": pytest.xfail("Redirect mode is at navigating importlib.resources.files") - if platform.system() == "Windows" and editable_mode == "inplace": + if platform.system() == "Windows" and editable.mode == "inplace": pytest.xfail("Windows fails to import the top-level extension module") - _setup_package_for_editable_layout_tests( - monkeypatch, tmp_path, editable, editable_mode, isolated + isolated.install( + "-v", + *editable.flags, + ".", ) isolated.execute( diff --git a/tests/test_hatchling.py b/tests/test_hatchling.py index d4615f65..a1ee65c5 100644 --- a/tests/test_hatchling.py +++ b/tests/test_hatchling.py @@ -10,7 +10,8 @@ @pytest.mark.network @pytest.mark.integration -@pytest.mark.usefixtures("package_hatchling") +@pytest.mark.parametrize("package", {"hatchling"}, indirect=True) +@pytest.mark.usefixtures("package") def test_hatchling_sdist(isolated, tmp_path: Path) -> None: dist = tmp_path / "dist" isolated.install("build[virtualenv]") @@ -34,7 +35,8 @@ def test_hatchling_sdist(isolated, tmp_path: Path) -> None: @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration -@pytest.mark.usefixtures("package_hatchling") +@pytest.mark.parametrize("package", {"hatchling"}, indirect=True) +@pytest.mark.usefixtures("package") @pytest.mark.parametrize( "build_args", [(), ("--wheel",)], ids=["sdist_to_wheel", "wheel_directly"] ) diff --git a/tests/test_prepare_metadata.py b/tests/test_prepare_metadata.py index 1c6c7379..4bf57beb 100644 --- a/tests/test_prepare_metadata.py +++ b/tests/test_prepare_metadata.py @@ -15,7 +15,8 @@ from scikit_build_core.settings.skbuild_model import ScikitBuildSettings -@pytest.mark.usefixtures("package_simplest_c") +@pytest.mark.parametrize("package", {"simplest_c"}, indirect=True) +@pytest.mark.usefixtures("package") @pytest.mark.parametrize("editable", [True, False], ids=["editable", "wheel"]) def test_prepare_metadata_for_build(fp, editable): # Old versions of packaging call mac_ver via subprocess diff --git a/tests/test_pyproject_extra_dirs.py b/tests/test_pyproject_extra_dirs.py index 2c93ea28..d7be0658 100644 --- a/tests/test_pyproject_extra_dirs.py +++ b/tests/test_pyproject_extra_dirs.py @@ -11,7 +11,8 @@ @pytest.mark.compile @pytest.mark.configure -@pytest.mark.usefixtures("package_filepath_pure") +@pytest.mark.parametrize("package", {"filepath_pure"}, indirect=True) +@pytest.mark.usefixtures("package") def test_pep517_wheel_extra_dirs(monkeypatch, tmp_path: Path): monkeypatch.setenv("SKBUILD_CMAKE_DEFINE", "SOME_DEFINE3=baz;SOME_DEFINE4=baz") monkeypatch.setenv("SKBUILD_CMAKE_ARGS", "-DSOME_ARGS1=baz") @@ -50,7 +51,8 @@ def test_pep517_wheel_extra_dirs(monkeypatch, tmp_path: Path): assert scripts == {"in_scripts.py"} -@pytest.mark.usefixtures("package_filepath_pure") +@pytest.mark.parametrize("package", {"filepath_pure"}, indirect=True) +@pytest.mark.usefixtures("package") def test_pep517_wheel_too_old_core(monkeypatch): monkeypatch.setenv("SKBUILD_CMAKE_DEFINE", "SOME_DEFINE3=baz;SOME_DEFINE4=baz") monkeypatch.setenv("SKBUILD_CMAKE_ARGS", "-DSOME_ARGS1=baz") diff --git a/tests/test_pyproject_pep517.py b/tests/test_pyproject_pep517.py index c7b730c9..ec648ea3 100644 --- a/tests/test_pyproject_pep517.py +++ b/tests/test_pyproject_pep517.py @@ -166,7 +166,10 @@ def each_unignored_file_ordered(*args, **kwargs): @pytest.mark.compile @pytest.mark.configure -@pytest.mark.usefixtures("package_simple_pyproject_script_with_flags") +@pytest.mark.parametrize( + "package", {"simple_pyproject_script_with_flags"}, indirect=True +) +@pytest.mark.usefixtures("package") @pytest.mark.parametrize( ("env_var", "setting"), [ @@ -248,7 +251,8 @@ def test_pep517_wheel(virtualenv, tmp_path: Path): @pytest.mark.compile @pytest.mark.configure -@pytest.mark.usefixtures("package_simple_pyproject_source_dir") +@pytest.mark.parametrize("package", {"simple_pyproject_source_dir"}, indirect=True) +@pytest.mark.usefixtures("package") def test_pep517_wheel_source_dir(virtualenv, tmp_path: Path): dist = tmp_path / "dist" out = build_wheel(str(dist), config_settings={"skbuild.wheel.build-tag": "1foo"}) @@ -367,7 +371,8 @@ def test_prepare_metdata_for_build_wheel_by_hand(tmp_path): assert len(metadata) == len(answer) -@pytest.mark.usefixtures("package_pep639_pure") +@pytest.mark.parametrize("package", {"pep639_pure"}, indirect=True) +@pytest.mark.usefixtures("package") def test_pep639_license_files_metadata(): metadata = build.util.project_wheel_metadata(str(Path.cwd()), isolated=False) answer = { @@ -384,7 +389,8 @@ def test_pep639_license_files_metadata(): assert len(metadata) == sum(len(v) for v in answer.values()) -@pytest.mark.usefixtures("package_pep639_pure") +@pytest.mark.parametrize("package", {"pep639_pure"}, indirect=True) +@pytest.mark.usefixtures("package") def test_pep639_license_files_sdist(tmp_path: Path): expected_metadata = ( inspect.cleandoc( @@ -424,7 +430,8 @@ def test_pep639_license_files_sdist(tmp_path: Path): assert pkg_info_contents == expected_metadata -@pytest.mark.usefixtures("package_pep639_pure") +@pytest.mark.parametrize("package", {"pep639_pure"}, indirect=True) +@pytest.mark.usefixtures("package") def test_pep639_license_files_wheel(tmp_path: Path): dist = tmp_path / "dist" out = build_wheel(str(dist), {}) diff --git a/tests/test_pyproject_pep518.py b/tests/test_pyproject_pep518.py index 91d551fe..328476f4 100644 --- a/tests/test_pyproject_pep518.py +++ b/tests/test_pyproject_pep518.py @@ -69,7 +69,8 @@ def test_pep518_sdist(isolated, package_simple_pyproject_ext, tmp_path: Path): @pytest.mark.network @pytest.mark.configure @pytest.mark.integration -@pytest.mark.usefixtures("package_sdist_config") +@pytest.mark.parametrize("package", {"sdist_config"}, indirect=True) +@pytest.mark.usefixtures("package") def test_pep518_sdist_with_cmake_config(isolated, cleanup_overwrite, tmp_path: Path): cleanup_overwrite.write_text("set(MY_VERSION fiddlesticks)") @@ -114,7 +115,8 @@ def test_pep518_sdist_with_cmake_config(isolated, cleanup_overwrite, tmp_path: P @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration -@pytest.mark.usefixtures("package_sdist_config") +@pytest.mark.parametrize("package", {"sdist_config"}, indirect=True) +@pytest.mark.usefixtures("package") @pytest.mark.parametrize( "build_args", [(), ("--wheel",)], ids=["sdist_to_wheel", "wheel_directly"] ) diff --git a/tests/test_pyproject_pep660.py b/tests/test_pyproject_pep660.py index 110bd680..f083c860 100644 --- a/tests/test_pyproject_pep660.py +++ b/tests/test_pyproject_pep660.py @@ -1,6 +1,5 @@ import sys import sysconfig -import typing import zipfile from pathlib import Path @@ -9,11 +8,6 @@ from scikit_build_core.build import build_editable -@pytest.fixture(params=["redirect", "inplace"]) -def editable_mode(request: pytest.FixtureRequest) -> str: - return typing.cast("str", request.param) - - # TODO: figure out why gmake is reporting no rule to make simple_pure.cpp @pytest.mark.compile @pytest.mark.configure @@ -22,10 +16,12 @@ def editable_mode(request: pytest.FixtureRequest) -> str: strict=False, reason="No idea why this fails on Cygwin", ) -@pytest.mark.usefixtures("package_simplest_c") -def test_pep660_wheel(editable_mode: str, tmp_path: Path): +@pytest.mark.parametrize("package", {"simplest_c"}, indirect=True) +@pytest.mark.parametrize("editable", ["redirect", "inplace"], indirect=True) +@pytest.mark.usefixtures("package") +def test_pep660_wheel(editable, tmp_path: Path): dist = tmp_path / "dist" - out = build_editable(str(dist), {"editable.mode": editable_mode}) + out = build_editable(str(dist), {"editable.mode": editable.mode}) (wheel,) = dist.glob("simplest-0.0.1-*.whl") assert wheel == dist / out @@ -35,9 +31,9 @@ def test_pep660_wheel(editable_mode: str, tmp_path: Path): with zf.open("simplest-0.0.1.dist-info/METADATA") as f: metadata = f.read().decode("utf-8") - assert len(file_names) == 4 if editable_mode == "redirect" else 2 + assert len(file_names) == 4 if editable.mode == "redirect" else 2 assert "simplest-0.0.1.dist-info" in file_names - if editable_mode == "redirect": + if editable.mode == "redirect": assert "simplest" in file_names assert "_simplest_editable.py" in file_names else: @@ -53,22 +49,14 @@ def test_pep660_wheel(editable_mode: str, tmp_path: Path): @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration -@pytest.mark.parametrize("isolate", [True, False], ids=["isolated", "not_isolated"]) -@pytest.mark.usefixtures("package_simplest_c") -def test_pep660_pip_isolated(isolated, isolate, editable_mode: str): - isolate_args = ["--no-build-isolation"] if not isolate else [] - isolated.install("pip>=23") - if not isolate: - isolated.install("scikit-build-core") - - build_dir = "" if editable_mode == "inplace" else "build/{wheel_tag}" - +@pytest.mark.parametrize("package", {"simplest_c"}, indirect=True) +@pytest.mark.parametrize("editable", ["redirect", "inplace"], indirect=True) +@pytest.mark.usefixtures("package") +def test_pep660_pip_isolated(isolated, isolate, editable): isolated.install( "-v", - f"--config-settings=build-dir={build_dir}", - f"--config-settings=editable.mode={editable_mode}", - *isolate_args, - "-e", + *isolate.flags, + *editable.flags, ".", ) @@ -85,7 +73,7 @@ def test_pep660_pip_isolated(isolated, isolate, editable_mode: str): assert any(x.samefile(python_source) for x in locations) cmake_install = isolated.platlib.joinpath("simplest").resolve() - if editable_mode == "redirect": + if editable.mode == "redirect": # Second path is from the CMake install assert any(x.samefile(cmake_install) for x in locations) @@ -104,7 +92,7 @@ def test_pep660_pip_isolated(isolated, isolate, editable_mode: str): else: ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") - module_source = python_source if editable_mode == "inplace" else cmake_install + module_source = python_source if editable.mode == "inplace" else cmake_install module_file = module_source / f"_module{ext_suffix}" # Windows FindPython may produce the wrong extension diff --git a/tests/test_pyproject_purelib.py b/tests/test_pyproject_purelib.py index 710a1542..b0e5fa62 100644 --- a/tests/test_pyproject_purelib.py +++ b/tests/test_pyproject_purelib.py @@ -7,7 +7,8 @@ @pytest.mark.compile @pytest.mark.configure -@pytest.mark.usefixtures("package_simple_purelib_package") +@pytest.mark.parametrize("package", {"simple_purelib_package"}, indirect=True) +@pytest.mark.usefixtures("package") def test_pep517_wheel(virtualenv, tmp_path: Path): dist = tmp_path / "dist" out = build_wheel(str(dist), {}) diff --git a/tests/test_setuptools_pep517.py b/tests/test_setuptools_pep517.py index b4eb4447..8d425c62 100644 --- a/tests/test_setuptools_pep517.py +++ b/tests/test_setuptools_pep517.py @@ -13,7 +13,8 @@ setuptools_version = Version(importlib.metadata.version("setuptools")) -@pytest.mark.usefixtures("package_simple_setuptools_ext") +@pytest.mark.parametrize("package", {"simple_setuptools_ext"}, indirect=True) +@pytest.mark.usefixtures("package") def test_pep517_sdist(tmp_path: Path): correct_metadata = textwrap.dedent( """\ @@ -68,7 +69,8 @@ def test_pep517_sdist(tmp_path: Path): @pytest.mark.compile @pytest.mark.configure @pytest.mark.broken_on_urct -@pytest.mark.usefixtures("package_simple_setuptools_ext") +@pytest.mark.parametrize("package", {"simple_setuptools_ext"}, indirect=True) +@pytest.mark.usefixtures("package") def test_pep517_wheel(virtualenv, tmp_path: Path): dist = tmp_path / "dist" out = build_wheel(str(dist)) @@ -97,7 +99,8 @@ def test_pep517_wheel(virtualenv, tmp_path: Path): assert add.strip() == "3" -@pytest.mark.usefixtures("package_toml_setuptools_ext") +@pytest.mark.parametrize("package", {"toml_setuptools_ext"}, indirect=True) +@pytest.mark.usefixtures("package") @pytest.mark.skipif( setuptools_version < Version("61.0"), reason="Requires setuptools 61+" ) @@ -148,7 +151,8 @@ def test_toml_sdist(tmp_path: Path): @pytest.mark.compile @pytest.mark.configure -@pytest.mark.usefixtures("package_toml_setuptools_ext") +@pytest.mark.parametrize("package", {"toml_setuptools_ext"}, indirect=True) +@pytest.mark.usefixtures("package") @pytest.mark.skipif( setuptools_version < Version("61.0"), reason="Requires setuptools 61+" ) @@ -182,7 +186,8 @@ def test_toml_wheel(virtualenv, tmp_path: Path): @pytest.mark.compile @pytest.mark.configure -@pytest.mark.usefixtures("package_mixed_setuptools") +@pytest.mark.parametrize("package", {"mixed_setuptools"}, indirect=True) +@pytest.mark.usefixtures("package") def test_mixed_wheel(virtualenv, tmp_path: Path): dist = tmp_path / "dist" out = build_wheel(str(dist)) diff --git a/tests/test_setuptools_pep518.py b/tests/test_setuptools_pep518.py index cf1d066e..2f070566 100644 --- a/tests/test_setuptools_pep518.py +++ b/tests/test_setuptools_pep518.py @@ -17,7 +17,8 @@ reason="Cygwin fails here with ld errors", strict=False, ) -@pytest.mark.usefixtures("package_simple_setuptools_ext") +@pytest.mark.parametrize("package", {"simple_setuptools_ext"}, indirect=True) +@pytest.mark.usefixtures("package") def test_pep518_wheel(isolated, tmp_path: Path): dist = tmp_path / "dist" isolated.install("build[virtualenv]") @@ -55,7 +56,8 @@ def test_pep518_wheel(isolated, tmp_path: Path): reason="Cygwin fails here with ld errors", strict=False, ) -@pytest.mark.usefixtures("package_simple_setuptools_ext") +@pytest.mark.parametrize("package", {"simple_setuptools_ext"}, indirect=True) +@pytest.mark.usefixtures("package") def test_pep518_pip(isolated): isolated.install("-v", ".")