Skip to content

Commit aa0cfcb

Browse files
mnadzamLalatenduMohanty
authored andcommitted
test(context): add coverage for WorkContext
- Add dedicated unit tests for setup(), package_build_info(), enable_parallel_builds(), wheels_build, pip_wheel_server_args, uv_clean_cache(), write_to_graph_to_file(), and clean_build_dirs() in context.py. - Decompose shared setup into _make_context() and _all_setup_dirs(). - Increase unit test coverage of context.py from 67% to 94%. Closes #1075 Signed-off-by: Marcel Nadzam <mnadzam@redhat.com> Co-authored-by: Cursor
1 parent 1429c1a commit aa0cfcb

1 file changed

Lines changed: 203 additions & 13 deletions

File tree

tests/test_context.py

Lines changed: 203 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,220 @@
11
import os
22
import pathlib
3+
from unittest.mock import Mock, patch
4+
5+
import pytest
6+
from packaging.requirements import Requirement
37

48
from fromager import context
59

610

7-
def test_pip_constraints_args(tmp_path: pathlib.Path) -> None:
8-
constraints_file = tmp_path / "constraints.txt"
9-
constraints_file.write_text("\n") # the file has to exist
10-
ctx = context.WorkContext(
11+
def _make_context(
12+
tmp_path: pathlib.Path,
13+
constraints_file: str | None = None,
14+
wheel_server_url: str = "",
15+
cleanup: bool = True,
16+
) -> context.WorkContext:
17+
return context.WorkContext(
1118
active_settings=None,
12-
constraints_file=str(constraints_file),
19+
constraints_file=constraints_file,
1320
patches_dir=tmp_path / "overrides/patches",
1421
sdists_repo=tmp_path / "sdists-repo",
1522
wheels_repo=tmp_path / "wheels-repo",
1623
work_dir=tmp_path / "work-dir",
24+
wheel_server_url=wheel_server_url,
25+
cleanup=cleanup,
1726
)
27+
28+
29+
def _all_setup_dirs(ctx: context.WorkContext) -> list[pathlib.Path]:
30+
return [
31+
ctx.work_dir,
32+
ctx.sdists_repo,
33+
ctx.sdists_downloads,
34+
ctx.sdists_builds,
35+
ctx.wheels_repo,
36+
ctx.wheels_downloads,
37+
ctx.wheels_prebuilt,
38+
ctx.wheels_build,
39+
ctx.uv_cache,
40+
ctx.logs_dir,
41+
]
42+
43+
44+
def test_pip_constraints_args(tmp_path: pathlib.Path) -> None:
45+
constraints_file = tmp_path / "constraints.txt"
46+
constraints_file.write_text("\n") # the file has to exist
47+
ctx = _make_context(tmp_path, constraints_file=str(constraints_file))
1848
ctx.setup()
1949
assert ["--constraint", os.fspath(constraints_file)] == ctx.pip_constraint_args
2050

21-
ctx = context.WorkContext(
22-
active_settings=None,
23-
constraints_file=None,
24-
patches_dir=tmp_path / "overrides/patches",
25-
sdists_repo=tmp_path / "sdists-repo",
26-
wheels_repo=tmp_path / "wheels-repo",
27-
work_dir=tmp_path / "work-dir",
28-
)
51+
ctx = _make_context(tmp_path)
2952
ctx.setup()
3053
assert [] == ctx.pip_constraint_args
54+
55+
56+
def test_setup_creates_directories(tmp_path: pathlib.Path) -> None:
57+
ctx = _make_context(tmp_path)
58+
ctx.setup()
59+
60+
for d in _all_setup_dirs(ctx):
61+
assert d.is_dir(), f"{d} was not created"
62+
63+
64+
def test_setup_is_idempotent(tmp_path: pathlib.Path) -> None:
65+
ctx = _make_context(tmp_path)
66+
ctx.setup()
67+
68+
test_file = ctx.logs_dir / "test_file.txt"
69+
test_file.write_text("test text")
70+
71+
ctx.setup()
72+
73+
for d in _all_setup_dirs(ctx):
74+
assert d.is_dir(), f"{d} was not created"
75+
assert test_file.read_text() == "test text"
76+
77+
78+
def test_package_build_info_extracts_name_from_requirement(
79+
tmp_context: context.WorkContext,
80+
) -> None:
81+
to_return = Mock()
82+
with patch.object(
83+
tmp_context.settings, "package_build_info", return_value=to_return
84+
) as mock_pbi:
85+
result = tmp_context.package_build_info(Requirement("numpy>=1.0"))
86+
87+
mock_pbi.assert_called_once_with("numpy")
88+
assert result is to_return
89+
90+
91+
def test_package_build_info_passes_string_directly(
92+
tmp_context: context.WorkContext,
93+
) -> None:
94+
to_return = Mock()
95+
with patch.object(
96+
tmp_context.settings, "package_build_info", return_value=to_return
97+
) as mock_pbi:
98+
result = tmp_context.package_build_info("numpy")
99+
100+
mock_pbi.assert_called_once_with("numpy")
101+
assert result is to_return
102+
103+
104+
def test_wheels_build_default(tmp_context: context.WorkContext) -> None:
105+
assert tmp_context.wheels_build == tmp_context.wheels_build_base
106+
107+
108+
def test_wheels_build_parallel(tmp_context: context.WorkContext) -> None:
109+
tmp_context.enable_parallel_builds()
110+
111+
result = tmp_context.wheels_build
112+
113+
assert result.parent == tmp_context.wheels_build_base
114+
assert result.is_dir()
115+
116+
117+
def test_write_to_graph_to_file(tmp_path: pathlib.Path) -> None:
118+
ctx = _make_context(tmp_path)
119+
ctx.setup()
120+
121+
with patch.object(ctx.dependency_graph, "serialize") as mock_serialize:
122+
ctx.write_to_graph_to_file()
123+
124+
mock_serialize.assert_called_once()
125+
assert ctx.graph_file.exists()
126+
127+
128+
def test_pip_wheel_server_args_https(tmp_path: pathlib.Path) -> None:
129+
ctx = _make_context(tmp_path, wheel_server_url="https://wheels.example.com/simple")
130+
assert ctx.pip_wheel_server_args == [
131+
"--index-url",
132+
"https://wheels.example.com/simple",
133+
]
134+
135+
136+
def test_pip_wheel_server_args_http(tmp_path: pathlib.Path) -> None:
137+
ctx = _make_context(tmp_path, wheel_server_url="http://wheels.example.com/simple")
138+
assert ctx.pip_wheel_server_args == [
139+
"--index-url",
140+
"http://wheels.example.com/simple",
141+
"--trusted-host",
142+
"wheels.example.com",
143+
]
144+
145+
146+
def test_uv_clean_cache_no_args_raises(tmp_path: pathlib.Path) -> None:
147+
ctx = _make_context(tmp_path)
148+
ctx.setup()
149+
150+
with pytest.raises(ValueError):
151+
ctx.uv_clean_cache()
152+
153+
154+
@patch("fromager.context.external_commands.run")
155+
def test_uv_clean_cache_calls_run(mock_run: Mock, tmp_path: pathlib.Path) -> None:
156+
ctx = _make_context(tmp_path)
157+
ctx.setup()
158+
159+
ctx.uv_clean_cache(Requirement("numpy"), Requirement("torch"))
160+
161+
mock_run.assert_called_once()
162+
cmd = mock_run.call_args[0][0]
163+
assert cmd == ["uv", "clean", "cache", "numpy", "torch"]
164+
extra_env = mock_run.call_args[1]["extra_environ"]
165+
assert extra_env["UV_CACHE_DIR"] == str(ctx.uv_cache)
166+
167+
168+
def test_clean_build_dirs_raises_when_env_is_child_of_sdist(
169+
tmp_path: pathlib.Path,
170+
) -> None:
171+
ctx = _make_context(tmp_path)
172+
ctx.setup()
173+
174+
sdist_root = tmp_path / "source"
175+
sdist_root.mkdir()
176+
build_env = Mock()
177+
build_env.path = sdist_root / "venv"
178+
build_env.path.mkdir()
179+
180+
with pytest.raises(ValueError):
181+
ctx.clean_build_dirs(sdist_root_dir=sdist_root, build_env=build_env)
182+
183+
assert sdist_root.is_dir()
184+
assert build_env.path.is_dir()
185+
186+
187+
def test_clean_build_dirs_removes_dirs_when_cleanup_enabled(
188+
tmp_path: pathlib.Path,
189+
) -> None:
190+
ctx = _make_context(tmp_path, cleanup=True)
191+
ctx.setup()
192+
193+
sdist_root = tmp_path / "source"
194+
sdist_root.mkdir()
195+
build_env = Mock()
196+
build_env.path = tmp_path / "build-env"
197+
build_env.path.mkdir()
198+
199+
ctx.clean_build_dirs(sdist_root_dir=sdist_root, build_env=build_env)
200+
201+
assert not sdist_root.exists()
202+
assert not build_env.path.exists()
203+
204+
205+
def test_clean_build_dirs_keeps_dirs_when_cleanup_disabled(
206+
tmp_path: pathlib.Path,
207+
) -> None:
208+
ctx = _make_context(tmp_path, cleanup=False)
209+
ctx.setup()
210+
211+
sdist_root = tmp_path / "source"
212+
sdist_root.mkdir()
213+
build_env = Mock()
214+
build_env.path = tmp_path / "build-env"
215+
build_env.path.mkdir()
216+
217+
ctx.clean_build_dirs(sdist_root_dir=sdist_root, build_env=build_env)
218+
219+
assert sdist_root.exists()
220+
assert build_env.path.exists()

0 commit comments

Comments
 (0)