Skip to content

Commit 4c5e770

Browse files
committed
Rewrite test cases for venv install, add new tests
1 parent 40f4a99 commit 4c5e770

File tree

3 files changed

+194
-16
lines changed

3 files changed

+194
-16
lines changed

tests/test_venv_install.py

Lines changed: 89 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1+
import itertools
12
import re
23
import subprocess
4+
from collections.abc import Collection
35
from itertools import chain
46
from pathlib import Path
57

68
import pytest
7-
from pytest_cases import parametrize_with_cases
9+
from pytest_cases import parametrize, parametrize_with_cases
810

911
from tests.helpers import run_command, write_files
10-
from tests.test_venv_install_cases import CasesVenvInstallRequirementstxt, CasesVenvInstallWithLock
11-
from tests.types import RequirementsDict, RequirementsStem
12+
from tests.test_venv_install_cases import (
13+
CasesVenvAddPackagesToRequirements,
14+
CasesVenvInstallRequirementstxt,
15+
CasesVenvInstallWithLock,
16+
)
17+
from tests.types import PackageName, PackageSpec, RequirementsDict, RequirementsStem
1218

1319
_package_name_regex = re.compile(r"^([a-zA-Z0-9_-]+)\b")
1420

@@ -23,34 +29,100 @@ def _check_package_was_installed(requirement: str, installed_line: str) -> None:
2329
assert package_name in installed_line, f"Package {package_name} was not installed succesfully"
2430

2531

32+
def _check_packages_in_requirements_txt(packages: Collection[str], requirements_file: Path) -> None:
33+
"""Check that the package was added to the requirements file"""
34+
requirements = requirements_file.read_text().strip()
35+
for package in packages:
36+
assert package in requirements, f"Package {package} was not added to requirements.txt"
37+
38+
2639
def test_venv_install_not_activated(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
2740
with pytest.raises(subprocess.CalledProcessError), monkeypatch.context() as m:
2841
m.delenv("VIRTUAL_ENV", raising=False)
2942
run_command(["venv install"], cwd=tmp_path, activated=False)
3043

3144

45+
_pjl = ["python-json-logger", "python-json-logger==2.0.7", "'python-json-logger == 2.0.7'"]
46+
_urllib = ["urllib3", "urllib3==2.2.1", "'urllib3 == 2.2.1'"]
47+
_wheel = ["wheel", "wheel==0.42.0", "'wheel == 0.42.0'"]
48+
49+
50+
@pytest.mark.order(after="test_venv_activate.py::test_venv_activate")
51+
@parametrize(
52+
["package_spec", "expected_packages"],
53+
[
54+
*itertools.product(itertools.product(_pjl), [("python-json-logger",)]),
55+
*itertools.product(itertools.product(_pjl, _urllib), [("python-json-logger", "urllib3")]),
56+
*itertools.product(itertools.product(_pjl, _urllib, _wheel), [("python-json-logger", "urllib3", "wheel")]),
57+
],
58+
)
59+
def test_venv_install_packages(
60+
package_spec: tuple[str, ...],
61+
expected_packages: tuple[str, ...],
62+
tmp_path: Path,
63+
capfd: pytest.CaptureFixture[str],
64+
):
65+
# Install the requirements
66+
run_command(
67+
f"venv install {' '.join(package_spec)} --skip-lock",
68+
cwd=tmp_path,
69+
activated=True,
70+
)
71+
72+
# Check pip install log output
73+
output = capfd.readouterr().out
74+
assert f"Installing" in output
75+
76+
# Check that the expected packages were installed
77+
installed_line = [line for line in output.splitlines() if line.startswith("Successfully installed")][0]
78+
for package in expected_packages:
79+
_check_package_was_installed(requirement=package, installed_line=installed_line)
80+
81+
# Check that the expected packages were added to requirements.txt
82+
_check_packages_in_requirements_txt(packages=expected_packages, requirements_file=tmp_path / "requirements.txt")
83+
84+
85+
@parametrize_with_cases(argnames=["files", "requirements"], cases=CasesVenvAddPackagesToRequirements)
86+
def test_add_packages_to_requirements(
87+
files: RequirementsDict,
88+
requirements: tuple[RequirementsStem, list[tuple[PackageName, PackageSpec]]],
89+
tmp_path: Path,
90+
capfd: pytest.CaptureFixture[str],
91+
):
92+
write_files(files=files, dir=tmp_path)
93+
94+
requirements_stem, name_spec = requirements
95+
requirements_filename = f"{requirements_stem.value}.txt"
96+
97+
_, package_specs = zip(*name_spec)
98+
package_spec_str = " ".join(f"'{package_spec}'" for package_spec in package_specs)
99+
100+
run_command(
101+
f"venv::_add_packages_to_requirements {requirements_filename} {package_spec_str}",
102+
cwd=tmp_path,
103+
activated=True,
104+
)
105+
new_contents = (tmp_path / requirements_filename).read_text().strip()
106+
107+
output = capfd.readouterr().out
108+
for package_name, package_spec in name_spec:
109+
assert f"Adding '{package_spec}' to {requirements_filename}" in output
110+
assert package_spec in new_contents, f"Package {package_name} was not added to {requirements_filename}"
111+
112+
32113
@pytest.mark.order(after="test_venv_activate.py::test_venv_activate")
33114
@parametrize_with_cases(argnames=["files", "requirements_stem"], cases=CasesVenvInstallRequirementstxt)
34-
@pytest.mark.parametrize("use_file_name", [True, False])
35115
def test_venv_install_requirements(
36116
files: RequirementsDict,
37117
requirements_stem: RequirementsStem,
38-
use_file_name: bool,
39118
tmp_path: Path,
40119
capfd: pytest.CaptureFixture[str],
41120
):
42121
write_files(files=files, dir=tmp_path)
43122

44123
# Install the requirements
45-
if not (requirements_stem is RequirementsStem.requirements or use_file_name):
46-
pytest.skip(f"Empty file name case only valid for requirements.txt, not {requirements_stem.value}.txt")
47-
48-
install_file_name = ""
49-
if use_file_name:
50-
install_file_name = f"{requirements_stem.value}.txt"
51-
52124
run_command(
53-
f"venv install {install_file_name} --skip-lock",
125+
f"venv install -r {requirements_stem.value}.txt --skip-lock",
54126
cwd=tmp_path,
55127
activated=True,
56128
)
@@ -71,15 +143,17 @@ def test_venv_install_requirements(
71143

72144
@pytest.mark.order(after="test_venv_activate.py::test_venv_activate")
73145
@parametrize_with_cases(argnames=["files", "requirements_stem"], cases=CasesVenvInstallWithLock)
74-
def test_venv_install_with_lock(
146+
def test_venv_install_requirements_with_lock(
75147
files: RequirementsDict,
76148
requirements_stem: RequirementsStem,
77149
tmp_path: Path,
78150
):
79151
write_files(files=files, dir=tmp_path)
80152

81153
run_command(
82-
f"venv install {requirements_stem}.txt",
154+
f"venv install -r {requirements_stem}.txt",
83155
cwd=tmp_path,
84156
activated=True,
85157
)
158+
159+
assert (tmp_path / f"{requirements_stem}.lock").exists()

tests/test_venv_install_cases.py

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
import itertools
2+
3+
from pytest_cases import parametrize
4+
15
from tests.helpers import collect_requirements
2-
from tests.types import RawFilesDict, RequirementsStem
6+
from tests.types import PackageName, PackageSpec, RawFilesDict, RequirementsStem
37

48

59
class CasesVenvInstallRequirementstxt:
@@ -94,3 +98,100 @@ def case_pypi(self) -> tuple[RawFilesDict, RequirementsStem]:
9498

9599
files = {"requirements.txt": requirements_txt}
96100
return files, RequirementsStem.requirements
101+
102+
@collect_requirements
103+
def case_pypi_dev(self) -> tuple[RawFilesDict, RequirementsStem]:
104+
requirements_txt = [
105+
"python-json-logger==2.0.7",
106+
]
107+
108+
dev_requirements_txt = [
109+
"-r requirements.txt",
110+
"numpy==1.26.0",
111+
]
112+
113+
files = {
114+
"requirements.txt": requirements_txt,
115+
"dev-requirements.txt": dev_requirements_txt,
116+
}
117+
return files, RequirementsStem.dev_requirements
118+
119+
120+
class CasesVenvAddPackagesToRequirements:
121+
@parametrize("version", ["", "==2.0.7", " == 2.0.7"])
122+
@collect_requirements
123+
def case_empty(
124+
self, version: str
125+
) -> tuple[RawFilesDict, tuple[RequirementsStem, list[tuple[PackageName, PackageSpec]]]]:
126+
requirements_txt: list[str] = []
127+
128+
name_spec: list[tuple[PackageName, PackageSpec]] = [
129+
("python-json-logger", f"python-json-logger{version}"),
130+
]
131+
132+
files = {"requirements.txt": requirements_txt}
133+
return files, (RequirementsStem.requirements, name_spec)
134+
135+
@parametrize("version", ["", "==2.0.7", " == 2.0.7"])
136+
@collect_requirements
137+
def case_has_one_add_one(
138+
self, version: str
139+
) -> tuple[RawFilesDict, tuple[RequirementsStem, list[tuple[PackageName, PackageSpec]]]]:
140+
requirements_txt = [
141+
"numpy==1.26.0",
142+
]
143+
144+
name_spec: list[tuple[PackageName, PackageSpec]] = [
145+
("python-json-logger", f"python-json-logger{version}"),
146+
]
147+
148+
files = {"requirements.txt": requirements_txt}
149+
return files, (RequirementsStem.requirements, name_spec)
150+
151+
@parametrize("version", ["", "==2.0.7", " == 2.0.7"])
152+
@collect_requirements
153+
def case_has_several_add_one(
154+
self, version: str
155+
) -> tuple[RawFilesDict, tuple[RequirementsStem, list[tuple[PackageName, PackageSpec]]]]:
156+
core_txt = [
157+
"wheel",
158+
]
159+
160+
requirements_txt = [
161+
"-r core.txt",
162+
"numpy==1.26.0",
163+
"requests",
164+
]
165+
166+
name_spec: list[tuple[PackageName, PackageSpec]] = [
167+
("python-json-logger", f"python-json-logger{version}"),
168+
]
169+
170+
files = {"core.txt": core_txt, "requirements.txt": requirements_txt}
171+
return files, (RequirementsStem.requirements, name_spec)
172+
173+
@parametrize(
174+
"versions",
175+
itertools.product(["", "==2.2.1", " == 2.2.1"], ["", "==2.0.7", " == 2.0.7"]),
176+
)
177+
@collect_requirements
178+
def case_has_several_add_several(
179+
self, versions: tuple[str, str]
180+
) -> tuple[RawFilesDict, tuple[RequirementsStem, list[tuple[PackageName, PackageSpec]]]]:
181+
core_txt = [
182+
"wheel",
183+
]
184+
185+
requirements_txt = [
186+
"-r core.txt",
187+
"numpy==1.26.0",
188+
"requests",
189+
]
190+
191+
name_spec: list[tuple[PackageName, PackageSpec]] = [
192+
("urllib3", f"urllib3{versions[0]}"),
193+
("python-json-logger", f"python-json-logger{versions[1]}"),
194+
]
195+
196+
files = {"core.txt": core_txt, "requirements.txt": requirements_txt}
197+
return files, (RequirementsStem.requirements, name_spec)

tests/types.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
RequirementsDict = dict[str, str]
88
RequirementFiles = dict[str, Path]
99

10+
PackageName = str
11+
PackageSpec = str
12+
1013

1114
class RequirementsStem(str, Enum):
1215
requirements = "requirements"

0 commit comments

Comments
 (0)