Skip to content

Commit

Permalink
🧪 uv tests
Browse files Browse the repository at this point in the history
  • Loading branch information
juftin committed Feb 22, 2024
1 parent 675c165 commit 9721b45
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 61 deletions.
60 changes: 44 additions & 16 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
Shared fixtures for tests.
"""

from __future__ import annotations

import contextlib
import os
import pathlib
import shutil
from dataclasses import dataclass, field
from subprocess import CompletedProcess
from typing import Dict, Generator, Type
from typing import Generator
from unittest.mock import patch

import pytest
Expand All @@ -19,7 +21,6 @@
from hatch.utils.fs import Path, temp_directory
from hatch.utils.platform import Platform

from hatch_pip_compile.installer import PipInstaller, PipSyncInstaller, PluginInstaller
from hatch_pip_compile.plugin import PipCompileEnvironment


Expand Down Expand Up @@ -120,16 +121,20 @@ def __post_init__(self) -> None:
self.default_environment = self.reload_environment("default")
self.test_environment = self.reload_environment("test")

def reload_environment(self, environment: str) -> PipCompileEnvironment:
def reload_environment(self, environment: str | PipCompileEnvironment) -> PipCompileEnvironment:
"""
Reload a new environment given the current state of the isolated project
"""
if isinstance(environment, PipCompileEnvironment):
environment_name = environment.name
else:
environment_name = environment
new_project = Project(self.isolation)
return PipCompileEnvironment(
root=self.isolation,
metadata=new_project.metadata,
name=environment,
config=new_project.config.envs[environment],
name=environment_name,
config=new_project.config.envs[environment_name],
matrix_variables={},
data_directory=self.isolated_data_dir,
isolated_data_directory=self.isolated_data_dir,
Expand All @@ -155,6 +160,36 @@ def chdir(self) -> Generator[None, None, None]:
finally:
os.chdir(current_dir)

def update_environment_resolver(
self, environment: str | PipCompileEnvironment, resolver: str
) -> PipCompileEnvironment:
"""
Update the environment resolver
"""
if isinstance(environment, PipCompileEnvironment):
environment_name = environment.name
else:
environment_name = environment
self.toml_doc["tool"]["hatch"]["envs"][environment_name]["pip-compile-resolver"] = resolver
self.update_pyproject()
return self.reload_environment(environment_name)

def update_environment_installer(
self, environment: str | PipCompileEnvironment, installer: str
) -> PipCompileEnvironment:
"""
Update the environment installer
"""
if isinstance(environment, PipCompileEnvironment):
environment_name = environment.name
else:
environment_name = environment
self.toml_doc["tool"]["hatch"]["envs"][environment_name][
"pip-compile-installer"
] = installer
self.update_pyproject()
return self.reload_environment(environment_name)


@pytest.fixture
def pip_compile(
Expand All @@ -176,20 +211,13 @@ def pip_compile(
)


@pytest.fixture
def installer_dict() -> Dict[str, Type[PluginInstaller]]:
"""
Installer dictionary for parametrized tests
"""
return {
"pip": PipInstaller,
"pip-sync": PipSyncInstaller,
}


@pytest.fixture(autouse=True)
def pip_compile_disable(monkeypatch: pytest.MonkeyPatch) -> None:
"""
Delete the PIP_COMPILE_DISABLE environment variable
"""
monkeypatch.delenv("PIP_COMPILE_DISABLE", raising=False)


resolver_param = pytest.mark.parametrize("resolver", ["pip-compile", "uv"])
installer_param = pytest.mark.parametrize("installer", ["pip", "pip-sync", "uv"])
6 changes: 4 additions & 2 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,11 @@ def test_cli_upgrade_packages(pip_compile: PipCompileFixture) -> None:
"""
runner = CliRunner()
with runner.isolated_filesystem(temp_dir=pip_compile.isolation):
result = runner.invoke(cli=cli, args=["--upgrade-package", "requests"])
result = runner.invoke(
cli=cli, args=["--upgrade-package", "requests", "--upgrade-package", "urllib3"]
)
assert result.exit_code == 0
assert "hatch-pip-compile: Upgrading packages: requests" in result.output
assert "hatch-pip-compile: Upgrading packages: requests, urllib3" in result.output


def test_cli_upgrade_test(pip_compile: PipCompileFixture) -> None:
Expand Down
20 changes: 9 additions & 11 deletions tests/test_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@
Installation Tests
"""

from typing import Dict, Type
from unittest.mock import Mock

import pytest

from hatch_pip_compile.exceptions import HatchPipCompileError
from hatch_pip_compile.installer import PluginInstaller
from tests.conftest import PipCompileFixture
from tests.conftest import PipCompileFixture, installer_param


def test_pip_install_dependencies(mock_check_command: Mock, pip_compile: PipCompileFixture) -> None:
Expand All @@ -33,17 +31,17 @@ def test_pip_install_dependencies(mock_check_command: Mock, pip_compile: PipComp
assert call_args == expected_call


@pytest.mark.parametrize("installer", ["pip", "pip-sync"])
def test_installer_type(
installer: str, installer_dict: Dict[str, Type[PluginInstaller]], pip_compile: PipCompileFixture
) -> None:
@installer_param
def test_installer_type(installer: str, pip_compile: PipCompileFixture) -> None:
"""
Test the `pip-compile-installer` configuration option
"""
pip_compile.toml_doc["tool"]["hatch"]["envs"]["default"]["pip-compile-installer"] = installer
pip_compile.update_pyproject()
updated_environment = pip_compile.reload_environment("default")
assert isinstance(updated_environment.installer, installer_dict[installer])
updated_environment = pip_compile.update_environment_installer(
environment="default", installer=installer
)
assert isinstance(
updated_environment.installer, updated_environment.dependency_installers[installer]
)


def test_installer_unknown(pip_compile: PipCompileFixture) -> None:
Expand Down
73 changes: 47 additions & 26 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,42 @@
"""

import sys
from typing import Dict, Type

import hatch
import packaging.requirements
import pytest

from hatch_pip_compile.installer import PluginInstaller
from tests.conftest import PipCompileFixture
from tests.conftest import PipCompileFixture, installer_param, resolver_param

try:
match_major, hatch_minor, _ = hatch._version.__version__.split(".")
except AttributeError:
match_major, hatch_minor, _ = hatch.__about__.__version__.split(".")


@pytest.mark.parametrize("installer", ["pip", "pip-sync"])
@installer_param
@resolver_param
def test_new_dependency(
installer: str, installer_dict: Dict[str, Type[PluginInstaller]], pip_compile: PipCompileFixture
installer: str,
pip_compile: PipCompileFixture,
resolver: str,
) -> None:
"""
Test adding a new dependency
"""
if installer == "pip-sync" and sys.platform == "win32": # pragma: no cover
pytest.skip("Flaky test on Windows")
original_requirements = pip_compile.default_environment.piptools_lock.read_header_requirements()
default_env = pip_compile.update_environment_resolver(environment="default", resolver=resolver)
original_requirements = default_env.piptools_lock.read_header_requirements()
assert original_requirements == [packaging.requirements.Requirement("hatch")]
pip_compile.toml_doc["project"]["dependencies"] = ["requests"]
pip_compile.toml_doc["tool"]["hatch"]["envs"]["default"]["pip-compile-installer"] = installer
pip_compile.update_pyproject()
updated_environment = pip_compile.reload_environment("default")
assert isinstance(updated_environment.installer, installer_dict[installer])
updated_environment = pip_compile.update_environment_installer(
environment=default_env, installer=installer
)
assert isinstance(
updated_environment.installer, updated_environment.dependency_installers[installer]
)
assert updated_environment.dependencies == ["requests"]
pip_compile.application.prepare_environment(environment=updated_environment)
assert updated_environment.lockfile_up_to_date is True
Expand All @@ -52,20 +57,28 @@ def test_new_dependency(
assert "requests" in result.stdout.decode()


@pytest.mark.parametrize("installer", ["pip", "pip-sync"])
@installer_param
@resolver_param
def test_delete_dependencies(
installer: str, installer_dict: Dict[str, Type[PluginInstaller]], pip_compile: PipCompileFixture
installer: str,
pip_compile: PipCompileFixture,
resolver: str,
) -> None:
"""
Test deleting all dependencies also deletes the lockfile
"""
if installer == "pip-sync" and sys.platform == "win32": # pragma: no cover
pytest.skip("Flaky test on Windows")
pip_compile.update_environment_resolver(
environment=pip_compile.default_environment, resolver=resolver
)
pip_compile.toml_doc["tool"]["hatch"]["envs"]["default"]["pip-compile-installer"] = installer
pip_compile.toml_doc["project"]["dependencies"] = []
pip_compile.update_pyproject()
updated_environment = pip_compile.reload_environment("default")
assert isinstance(updated_environment.installer, installer_dict[installer])
assert isinstance(
updated_environment.installer, updated_environment.dependency_installers[installer]
)
assert updated_environment.dependencies == []
assert updated_environment.lockfile_up_to_date is False
pip_compile.application.prepare_environment(environment=updated_environment)
Expand All @@ -75,10 +88,12 @@ def test_delete_dependencies(
assert updated_environment.piptools_lock_file.exists() is False


def test_create_constraint_environment(pip_compile: PipCompileFixture) -> None:
@resolver_param
def test_create_constraint_environment(pip_compile: PipCompileFixture, resolver: str) -> None:
"""
Syncing an environment with a constraint env also syncs the constraint env
"""
pip_compile.update_environment_resolver(environment="default", resolver=resolver)
original_requirements = pip_compile.default_environment.piptools_lock.read_header_requirements()
assert original_requirements == [packaging.requirements.Requirement("hatch")]
pip_compile.toml_doc["project"]["dependencies"] = ["requests"]
Expand All @@ -96,40 +111,44 @@ def test_create_constraint_environment(pip_compile: PipCompileFixture) -> None:
assert "pytest" in result.stdout.decode()


def test_dependency_uninstalled(pip_compile: PipCompileFixture) -> None:
@resolver_param
def test_dependency_uninstalled(pip_compile: PipCompileFixture, resolver: str) -> None:
"""
An environment is prepared, then a dependency is uninstalled,
the environment should be out of sync even though the lockfile
is good
"""
pip_compile.application.prepare_environment(environment=pip_compile.test_environment)
list_result = pip_compile.test_environment.plugin_check_command(
test_env = pip_compile.update_environment_resolver(environment="test", resolver=resolver)
pip_compile.application.prepare_environment(environment=test_env)
list_result = test_env.plugin_check_command(
command=["python", "-m", "pip", "list"],
capture_output=True,
)
assert "pytest" in list_result.stdout.decode()
assert pip_compile.test_environment.dependencies_in_sync() is True
pip_compile.test_environment.plugin_check_command(
assert test_env.dependencies_in_sync() is True
test_env.plugin_check_command(
command=["python", "-m", "pip", "uninstall", "pytest", "pytest-cov", "-y"],
)
new_list_result = pip_compile.test_environment.plugin_check_command(
new_list_result = test_env.plugin_check_command(
command=["python", "-m", "pip", "list"],
capture_output=True,
)
assert "pytest" not in new_list_result.stdout.decode()
assert pip_compile.test_environment.lockfile_up_to_date is True
assert pip_compile.test_environment.dependencies_in_sync() is False
assert test_env.lockfile_up_to_date is True
assert test_env.dependencies_in_sync() is False


def test_lockfile_missing(pip_compile: PipCompileFixture) -> None:
@resolver_param
def test_lockfile_missing(pip_compile: PipCompileFixture, resolver: str) -> None:
"""
Lockfile missing on previously prepared environment
"""
# Prepare the test environment, assert it is in sync
pip_compile.application.prepare_environment(environment=pip_compile.test_environment)
assert pip_compile.test_environment.dependencies_in_sync() is True
test_env = pip_compile.update_environment_resolver(environment="test", resolver=resolver)
pip_compile.application.prepare_environment(environment=test_env)
assert test_env.dependencies_in_sync() is True
# Delete the lockfile, assert environment is in sync but lockfile is missing
pip_compile.test_environment.piptools_lock_file.unlink()
test_env.piptools_lock_file.unlink()
updated_environment = pip_compile.reload_environment("test")
list_result = updated_environment.plugin_check_command(
command=["python", "-m", "pip", "list"],
Expand All @@ -156,10 +175,12 @@ def test_check_dependency_hash_creates_lock(pip_compile: PipCompileFixture) -> N
assert updated_environment.piptools_lock_file.exists() is True


def test_dependencies_in_sync(pip_compile: PipCompileFixture) -> None:
@resolver_param
def test_dependencies_in_sync(pip_compile: PipCompileFixture, resolver: str) -> None:
"""
Test the `dependencies_in_sync` method
"""
pip_compile.update_environment_resolver(environment="default", resolver=resolver)
pip_compile.default_environment.create()
assert pip_compile.default_environment.lockfile_up_to_date is True
assert pip_compile.default_environment.dependencies_in_sync() is False
Expand Down
18 changes: 13 additions & 5 deletions tests/test_integration_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,21 @@
from click.testing import CliRunner

from hatch_pip_compile.exceptions import HatchPipCompileError
from tests.conftest import PipCompileFixture
from tests.conftest import PipCompileFixture, resolver_param


@resolver_param
@pytest.mark.parametrize("environment_name", ["default", "test", "lint", "docs", "misc"])
def test_invoke_environment_creates_env(
pip_compile: PipCompileFixture, environment_name: str
pip_compile: PipCompileFixture, environment_name: str, resolver: str
) -> None:
"""
Test using the CLI runner
"""
runner = CliRunner()
environment = pip_compile.reload_environment(environment=environment_name)
environment = pip_compile.update_environment_resolver(
environment=environment_name, resolver=resolver
)
venv = environment.virtual_env.directory
assert not venv.exists()
with runner.isolated_filesystem(pip_compile.isolation):
Expand Down Expand Up @@ -98,13 +101,18 @@ def test_missing_lockfile_after_prepared(pip_compile: PipCompileFixture) -> None
assert environment.piptools_lock_file.exists()


@resolver_param
@pytest.mark.parametrize("environment_name", ["default", "test", "lint"])
def test_pip_compile_disable_cli(pip_compile: PipCompileFixture, environment_name: str) -> None:
def test_pip_compile_disable_cli(
pip_compile: PipCompileFixture, environment_name: str, resolver: str
) -> None:
"""
Test that the `PIP_COMPILE_DISABLE` environment variable raises an error
"""
runner = CliRunner()
environment = pip_compile.reload_environment(environment=environment_name)
environment = pip_compile.update_environment_resolver(
environment=environment_name, resolver=resolver
)
environment.piptools_lock_file.unlink(missing_ok=True)
with runner.isolated_filesystem(pip_compile.isolation):
result = runner.invoke(
Expand Down
Loading

0 comments on commit 9721b45

Please sign in to comment.