From 7a32ebffd5effce9de6242da010c81924378e509 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Thu, 14 Dec 2023 09:56:21 +0800 Subject: [PATCH] ci: update tests to use pdm.pytest plugin (#3) --- pdm.lock | 101 +++++++++---- pyproject.toml | 3 +- tests/conftest.py | 16 +- tests/unit/test_build_locked.py | 253 +++++++++++++++----------------- 4 files changed, 196 insertions(+), 177 deletions(-) diff --git a/pdm.lock b/pdm.lock index 7ea3b04..63e6aeb 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:6e1461ca3b27f25862f5855ab6c0a6c9bc96124c71bde72c2f06ae23bbcc56a9" +content_hash = "sha256:59ab9662eb1f8c898fb740c7aa0241adf24ac6d96d3b51e709710c14eeeb73e5" [[package]] name = "blinker" @@ -142,27 +142,13 @@ files = [ {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, ] -[[package]] -name = "click" -version = "8.1.6" -requires_python = ">=3.7" -summary = "Composable command line interface toolkit" -groups = ["dev"] -dependencies = [ - "colorama; platform_system == \"Windows\"", -] -files = [ - {file = "click-8.1.6-py3-none-any.whl", hash = "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"}, - {file = "click-8.1.6.tar.gz", hash = "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd"}, -] - [[package]] name = "colorama" version = "0.4.6" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" summary = "Cross-platform colored terminal text." groups = ["dev"] -marker = "platform_system == \"Windows\" or sys_platform == \"win32\"" +marker = "sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -293,6 +279,20 @@ files = [ {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, ] +[[package]] +name = "dep-logic" +version = "0.0.3" +requires_python = ">=3.8" +summary = "Python dependency specifications supporting logical operations" +groups = ["dev"] +dependencies = [ + "packaging>=22", +] +files = [ + {file = "dep_logic-0.0.3-py3-none-any.whl", hash = "sha256:7ac0055dbe980846631a06c8a895b9ace69e613c6c29e11c4976ccf29998fed3"}, + {file = "dep_logic-0.0.3.tar.gz", hash = "sha256:6b9da937652d8ecf63e916c254d8ec4e8e27f8bcf0f8df205c561f5b3d740e79"}, +] + [[package]] name = "distlib" version = "0.3.7" @@ -328,7 +328,7 @@ files = [ [[package]] name = "findpython" -version = "0.3.1" +version = "0.4.1" requires_python = ">=3.7" summary = "A utility to find python versions on your system" groups = ["dev"] @@ -336,8 +336,8 @@ dependencies = [ "packaging>=20", ] files = [ - {file = "findpython-0.3.1-py3-none-any.whl", hash = "sha256:b57a67fc95cc687bfcfa4944c730a5e368b8cbe20f84ad4cdbc9d6bfe128f50b"}, - {file = "findpython-0.3.1.tar.gz", hash = "sha256:7621f8a9c199a70d219831cddaeb84ca7e83e20b8358172bff47871a18a5eda4"}, + {file = "findpython-0.4.1-py3-none-any.whl", hash = "sha256:ca3a5272704b0b8a2f5e8d03d816701ec99f13eafee9bb2a316cbf099c937ede"}, + {file = "findpython-0.4.1.tar.gz", hash = "sha256:d7d014558681b3761d57a5b2342a713a8bf302f6c1fc9d99f81b9d8bd1681b04"}, ] [[package]] @@ -519,15 +519,16 @@ files = [ [[package]] name = "pdm" -version = "2.8.0" -requires_python = ">=3.7" +version = "2.11.0" +requires_python = ">=3.8" summary = "A modern Python package and dependency manager supporting the latest PEP standards" groups = ["dev"] dependencies = [ "blinker", "cachecontrol[filecache]>=0.13.0", "certifi", - "findpython>=0.3.0", + "dep-logic<1.0,>=0.0.2", + "findpython<1.0.0a0,>=0.4.0", "importlib-metadata>=3.6; python_version < \"3.10\"", "importlib-resources>=5; python_version < \"3.9\"", "installer<0.8,>=0.7", @@ -541,12 +542,30 @@ dependencies = [ "shellingham>=1.3.2", "tomli>=1.1.0; python_version < \"3.11\"", "tomlkit<1,>=0.11.1", - "unearth>=0.9.0", + "truststore; python_version >= \"3.10\"", + "unearth>=0.12.1", "virtualenv>=20", ] files = [ - {file = "pdm-2.8.0-py3-none-any.whl", hash = "sha256:5ddd0921de8054abaeb70149b63a0c29dfa5d12c561b3a774cf04c43a85f6f08"}, - {file = "pdm-2.8.0.tar.gz", hash = "sha256:060b1628fda465f2c41e064d212ca9eba630c346a3305e115ae23a4c2cf024da"}, + {file = "pdm-2.11.0-py3-none-any.whl", hash = "sha256:925569ab173d08a6cc171e0d3dd50e04016a0ef622e0799fa1bdbc283782213a"}, + {file = "pdm-2.11.0.tar.gz", hash = "sha256:3ff4b5d4d8af4747dde978e8d0fea8c5f5e65a07dee861d2c2fbafdcc8d69205"}, +] + +[[package]] +name = "pdm" +version = "2.11.0" +extras = ["pytest"] +requires_python = ">=3.8" +summary = "A modern Python package and dependency manager supporting the latest PEP standards" +groups = ["dev"] +dependencies = [ + "pdm==2.11.0", + "pytest", + "pytest-mock", +] +files = [ + {file = "pdm-2.11.0-py3-none-any.whl", hash = "sha256:925569ab173d08a6cc171e0d3dd50e04016a0ef622e0799fa1bdbc283782213a"}, + {file = "pdm-2.11.0.tar.gz", hash = "sha256:3ff4b5d4d8af4747dde978e8d0fea8c5f5e65a07dee861d2c2fbafdcc8d69205"}, ] [[package]] @@ -659,6 +678,20 @@ files = [ {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, ] +[[package]] +name = "pytest-mock" +version = "3.12.0" +requires_python = ">=3.8" +summary = "Thin-wrapper around the mock package for easier use with pytest" +groups = ["dev"] +dependencies = [ + "pytest>=5.0", +] +files = [ + {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, + {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, +] + [[package]] name = "python-dotenv" version = "1.0.0" @@ -819,6 +852,18 @@ files = [ {file = "tomlkit-0.11.8.tar.gz", hash = "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3"}, ] +[[package]] +name = "truststore" +version = "0.8.0" +requires_python = ">= 3.10" +summary = "Verify certificates using native system trust stores" +groups = ["dev"] +marker = "python_version >= \"3.10\"" +files = [ + {file = "truststore-0.8.0-py3-none-any.whl", hash = "sha256:e37a5642ae9fc48caa8f120b6283d77225d600d224965a672c9e8ef49ce4bb4c"}, + {file = "truststore-0.8.0.tar.gz", hash = "sha256:dc70da89634944a579bfeec70a7a4523c53ffdb3cf52d1bb4a431fda278ddb96"}, +] + [[package]] name = "typing-extensions" version = "4.7.1" @@ -833,7 +878,7 @@ files = [ [[package]] name = "unearth" -version = "0.10.0" +version = "0.12.1" requires_python = ">=3.7" summary = "A utility to fetch and download python packages" groups = ["dev"] @@ -842,8 +887,8 @@ dependencies = [ "requests>=2.25", ] files = [ - {file = "unearth-0.10.0-py3-none-any.whl", hash = "sha256:e8b36f6efd6b677a3ff86354675368a79e98ee8ba01aaa601858e2cc043bc51a"}, - {file = "unearth-0.10.0.tar.gz", hash = "sha256:d5b152a5ab2aa3e5149a11cf7b3ba5c5d493176dca38f66ca8969dadd5a8f645"}, + {file = "unearth-0.12.1-py3-none-any.whl", hash = "sha256:a5a5c51ca44965cbe3618116bd592bb0bbe3705af5fe14e5792660d904aad7c8"}, + {file = "unearth-0.12.1.tar.gz", hash = "sha256:4caad941b60f51e50fdc109866234d407910aef77f1233aa1b6b5d168c7427ee"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index 5dd556c..0fdaedd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,11 +46,10 @@ package-dir = "src" [tool.pdm.dev-dependencies] dev = [ - "pdm>=2.8.0", + "pdm[pytest]>=2.8.0", "pytest>=7.4.0", "pkginfo>=1.9.6", "pytest-cov>=4.1.0", - "click>=8.1.6", "pre-commit>=3.5.0", ] diff --git a/tests/conftest.py b/tests/conftest.py index ba4d4d8..ab9abcf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,11 +1,10 @@ """conftest""" import shutil from pathlib import Path -from typing import Any, Callable import pytest -from click.testing import CliRunner, Result -from pdm.core import Core + +pytest_plugins = ["pdm.pytest"] @pytest.fixture(name="repo_path") @@ -64,14 +63,3 @@ def fixture_data_base_path(tests_base_path: Path) -> Path: Path to the 'tests/data' folder. """ return tests_base_path.joinpath("data").resolve() - - -@pytest.fixture(scope="session") -def invoke() -> Callable[..., Result]: - """Wrapper for CLIRunner""" - runner = CliRunner(mix_stderr=False) - - def caller(args: Any) -> Result: - return runner.invoke(Core(), args, prog_name="pdm", catch_exceptions=False) - - return caller diff --git a/tests/unit/test_build_locked.py b/tests/unit/test_build_locked.py index eadc784..ae33687 100644 --- a/tests/unit/test_build_locked.py +++ b/tests/unit/test_build_locked.py @@ -1,14 +1,17 @@ """test pdm build --locked""" -import functools +from __future__ import annotations + import hashlib import re from pathlib import Path -from typing import Any, Callable +from typing import TYPE_CHECKING, Generator import pytest -from click.testing import Result from pkginfo import Wheel +if TYPE_CHECKING: + from pdm.pytest import PDMCallable + def wheel_from_tempdir(whl_dir: Path) -> Wheel: """ @@ -60,138 +63,122 @@ def get_pyproject_hash(project: Path) -> str: return hashlib.sha256(file.read()).hexdigest() # when >=3.11: use hashlib.file_digest instead -def assert_pyproject_unmodified(func: Callable[..., Any]) -> Callable[..., Any]: +@pytest.fixture +def assert_pyproject_unmodified(data_base_path: Path, test_project: str) -> Generator[None, None, None]: + """ + A pytest fixture to assert that pyproject.toml is reset properly to its original state + """ + project = data_base_path / test_project + hash_before = get_pyproject_hash(project) + yield + assert get_pyproject_hash(project) == hash_before, "pyproject.toml hashes do not match, check for modifications" + + +@pytest.mark.usefixtures("assert_pyproject_unmodified") +@pytest.mark.parametrize("test_project", ["simple-optional"]) +def test_build_locked_optional(pdm: PDMCallable, data_base_path: Path, temp_dir: Path, test_project: str) -> None: + """this project has optional dependencies, is not yet locked and has no setting in pyproject.toml + + Args: + pdm: PDM runner fixture + data_base_path: path to tests/data + temp_dir: path to tests/_temp/... temporary directory + test_project: path to test project + """ + project_path = data_base_path.joinpath(test_project) + cmd = ["build", "--locked", "--project", project_path.as_posix(), "--dest", temp_dir.as_posix()] + result = pdm(cmd) + assert result.exit_code == 0 + project_path.joinpath("pdm.lock").unlink() + + wheel = wheel_from_tempdir(temp_dir) + assert count_group_dependencies(wheel, "locked") == 5 + assert count_group_dependencies(wheel, "socks-locked") == 1 + assert count_group_dependencies(wheel, "test-locked") == 0 # dev-dependency test group should be skipped + + +@pytest.mark.usefixtures("assert_pyproject_unmodified") +@pytest.mark.parametrize("test_project", ["simple"]) +def test_build_locked(pdm: PDMCallable, data_base_path: Path, temp_dir: Path, test_project: str) -> None: + """this project has no optional dependencies, is not yet locked and has no setting in pyproject.toml + + Args: + pdm: PDM runner fixture + data_base_path: path to tests/data + temp_dir: path to tests/_temp/... temporary directory + test_project: path to test project + """ + project_path = data_base_path.joinpath(test_project) + cmd = ["build", "--locked", "--project", project_path.as_posix(), "--dest", temp_dir.as_posix()] + result = pdm(cmd) + assert result.exit_code == 0 + project_path.joinpath("pdm.lock").unlink() + + wheel = wheel_from_tempdir(temp_dir) + assert sum(1 for dist in wheel.requires_dist if "locked" in dist) == 5 + + +@pytest.mark.usefixtures("assert_pyproject_unmodified") +@pytest.mark.parametrize("test_project", ["simple-optional"]) +def test_build_normal(pdm: PDMCallable, data_base_path: Path, temp_dir: Path, test_project: str) -> None: + """test that not using --locked doesn't add locked dependencies + + Args: + pdm: PDM runner fixture + data_base_path: path to tests/data + temp_dir: path to tests/_temp/... temporary directory + test_project: path to test project """ - wrap tests to assert that pyproject.toml is reset properly to its original state + project_path = data_base_path.joinpath(test_project) + cmd = ["build", "--project", project_path.as_posix(), "--dest", temp_dir.as_posix()] + result = pdm(cmd) + assert result + + wheel = wheel_from_tempdir(temp_dir) + assert not any("locked" in dist for dist in wheel.requires_dist) + + +@pytest.mark.usefixtures("assert_pyproject_unmodified") +@pytest.mark.parametrize("test_project", ["large"]) +def test_build_locked_pyproject(pdm: PDMCallable, data_base_path: Path, temp_dir: Path, test_project: str) -> None: + """this project has a lockfile and the pyproject.toml setting tool.pdm.build.locked Args: - func: the test function + pdm: PDM runner fixture + data_base_path: path to tests/data + temp_dir: path to tests/_temp/... temporary directory + test_project: path to test project """ + project_path = data_base_path.joinpath(test_project).as_posix() + cmd = ["build", "--project", project_path, "--dest", temp_dir.as_posix()] + result = pdm(cmd) + assert result.exit_code == 0 + + wheel = wheel_from_tempdir(temp_dir) + assert count_group_dependencies(wheel, "locked") == 26 + assert count_group_dependencies(wheel, "extras-locked") == 1 + assert count_group_dependencies(wheel, "cow-locked") == 1 + - @functools.wraps(func) - def inner(*args: Any, **kwargs: Any) -> None: - project = kwargs["data_base_path"].joinpath(kwargs["test_project"]) - hash_before = get_pyproject_hash(project) - func(*args, **kwargs) - assert get_pyproject_hash(project) == hash_before, "pyproject.toml hashes do not match, check for modifications" - - return inner - - -class TestBuildLocked: - """test pdm build --locked""" - - @assert_pyproject_unmodified - @pytest.mark.parametrize("test_project", ["simple-optional"]) - def test_build_locked_optional( - self, invoke: Callable[..., Result], data_base_path: Path, temp_dir: Path, test_project: str - ) -> None: - """this project has optional dependencies, is not yet locked and has no setting in pyproject.toml - - Args: - invoke: CliRunner fixture - data_base_path: path to tests/data - temp_dir: path to tests/_temp/... temporary directory - test_project: path to test project - """ - project_path = data_base_path.joinpath(test_project) - cmd = ["build", "--locked", "--project", project_path.as_posix(), "--dest", temp_dir.as_posix()] - result = invoke(cmd) - assert result.exit_code == 0 - project_path.joinpath("pdm.lock").unlink() - - wheel = wheel_from_tempdir(temp_dir) - assert count_group_dependencies(wheel, "locked") == 5 - assert count_group_dependencies(wheel, "socks-locked") == 1 - assert count_group_dependencies(wheel, "test-locked") == 0 # dev-dependency test group should be skipped - - @assert_pyproject_unmodified - @pytest.mark.parametrize("test_project", ["simple"]) - def test_build_locked( - self, invoke: Callable[..., Result], data_base_path: Path, temp_dir: Path, test_project: str - ) -> None: - """this project has no optional dependencies, is not yet locked and has no setting in pyproject.toml - - Args: - invoke: CliRunner fixture - data_base_path: path to tests/data - temp_dir: path to tests/_temp/... temporary directory - test_project: path to test project - """ - project_path = data_base_path.joinpath(test_project) - cmd = ["build", "--locked", "--project", project_path.as_posix(), "--dest", temp_dir.as_posix()] - result = invoke(cmd) - assert result.exit_code == 0 - project_path.joinpath("pdm.lock").unlink() - - wheel = wheel_from_tempdir(temp_dir) - assert sum(1 for dist in wheel.requires_dist if "locked" in dist) == 5 - - @assert_pyproject_unmodified - @pytest.mark.parametrize("test_project", ["simple-optional"]) - def test_build_normal( - self, invoke: Callable[..., Result], data_base_path: Path, temp_dir: Path, test_project: str - ) -> None: - """test that not using --locked doesn't add locked dependencies - - Args: - invoke: CliRunner fixture - data_base_path: path to tests/data - temp_dir: path to tests/_temp/... temporary directory - test_project: path to test project - """ - project_path = data_base_path.joinpath(test_project) - cmd = ["build", "--project", project_path.as_posix(), "--dest", temp_dir.as_posix()] - result = invoke(cmd) - assert result - - wheel = wheel_from_tempdir(temp_dir) - assert not any("locked" in dist for dist in wheel.requires_dist) - - @assert_pyproject_unmodified - @pytest.mark.parametrize("test_project", ["large"]) - def test_build_locked_pyproject( - self, invoke: Callable[..., Result], data_base_path: Path, temp_dir: Path, test_project: str - ) -> None: - """this project has a lockfile and the pyproject.toml setting tool.pdm.build.locked - - Args: - invoke: CliRunner fixture - data_base_path: path to tests/data - temp_dir: path to tests/_temp/... temporary directory - test_project: path to test project - """ - project_path = data_base_path.joinpath(test_project).as_posix() - cmd = ["build", "--project", project_path, "--dest", temp_dir.as_posix()] - result = invoke(cmd) - assert result.exit_code == 0 - - wheel = wheel_from_tempdir(temp_dir) - assert count_group_dependencies(wheel, "locked") == 26 - assert count_group_dependencies(wheel, "extras-locked") == 1 - assert count_group_dependencies(wheel, "cow-locked") == 1 - - @assert_pyproject_unmodified - @pytest.mark.parametrize("test_project", ["invalid"]) - def test_build_locked_invalid( - self, invoke: Callable[..., Result], data_base_path: Path, temp_dir: Path, test_project: str - ) -> None: - """this project's lockfile has a group containing "-locked" and will thus error - - Args: - invoke: CliRunner fixture - data_base_path: path to tests/data - temp_dir: path to tests/_temp/... temporary directory - test_project: path to test project - """ - project_path_invalid = data_base_path.joinpath(test_project) - cmd = [ - "build", - "--locked", - "--project", - project_path_invalid.as_posix(), - "--dest", - temp_dir.as_posix(), - ] - result = invoke(cmd) - assert "PdmException" in result.stderr +@pytest.mark.usefixtures("assert_pyproject_unmodified") +@pytest.mark.parametrize("test_project", ["invalid"]) +def test_build_locked_invalid(pdm: PDMCallable, data_base_path: Path, temp_dir: Path, test_project: str) -> None: + """this project's lockfile has a group containing "-locked" and will thus error + + Args: + pdm: PDM runner fixture + data_base_path: path to tests/data + temp_dir: path to tests/_temp/... temporary directory + test_project: path to test project + """ + project_path_invalid = data_base_path.joinpath(test_project) + cmd = [ + "build", + "--locked", + "--project", + project_path_invalid.as_posix(), + "--dest", + temp_dir.as_posix(), + ] + result = pdm(cmd) + assert "PdmException" in result.stderr