Skip to content

Commit 60e5611

Browse files
committed
Add unit tests
1 parent a255cae commit 60e5611

5 files changed

Lines changed: 2275 additions & 0 deletions

File tree

test/test_builder.py

Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
import os
2+
import shutil
3+
from unittest import mock
4+
5+
import pytest
6+
7+
from cget.builder import Builder
8+
import cget.util as util
9+
10+
11+
class MockPrefix:
12+
"""Lightweight stand-in for CGetPrefix used by Builder tests."""
13+
def __init__(self, prefix_dir, verbose=False):
14+
self.prefix = prefix_dir
15+
self.verbose = verbose
16+
self.toolchain = os.path.join(prefix_dir, "cget", "cget.cmake")
17+
self.cmd = util.Commander(paths=[os.path.join(prefix_dir, "bin")], verbose=verbose)
18+
19+
def log(self, *args):
20+
pass
21+
22+
23+
# ── Builder construction ─────────────────────────────────────────────────────
24+
25+
class TestBuilderInit:
26+
def test_basic_init(self, tmp_path):
27+
prefix = MockPrefix(str(tmp_path))
28+
top = str(tmp_path / "build_top")
29+
os.makedirs(top)
30+
b = Builder(prefix, top)
31+
assert b.prefix is prefix
32+
assert b.top_dir == top
33+
assert b.build_dir == os.path.join(top, "build")
34+
assert b.exists is False
35+
assert b.cmake_original_file == '__cget_original_cmake_file__.cmake'
36+
37+
def test_exists_flag(self, tmp_path):
38+
prefix = MockPrefix(str(tmp_path))
39+
top = str(tmp_path / "build_top")
40+
os.makedirs(top)
41+
b = Builder(prefix, top, exists=True)
42+
assert b.exists is True
43+
44+
45+
# ── path helpers ─────────────────────────────────────────────────────────────
46+
47+
class TestBuilderPaths:
48+
def test_get_path(self, tmp_path):
49+
prefix = MockPrefix(str(tmp_path))
50+
top = str(tmp_path / "top")
51+
os.makedirs(top)
52+
b = Builder(prefix, top)
53+
assert b.get_path("foo") == os.path.join(top, "foo")
54+
assert b.get_path("a", "b") == os.path.join(top, "a", "b")
55+
56+
def test_get_build_path(self, tmp_path):
57+
prefix = MockPrefix(str(tmp_path))
58+
top = str(tmp_path / "top")
59+
os.makedirs(top)
60+
b = Builder(prefix, top)
61+
assert b.get_build_path("CMakeCache.txt") == os.path.join(top, "build", "CMakeCache.txt")
62+
63+
64+
# ── is_make_generator ────────────────────────────────────────────────────────
65+
66+
class TestIsMakeGenerator:
67+
def test_true_when_makefile_exists(self, tmp_path):
68+
prefix = MockPrefix(str(tmp_path))
69+
top = str(tmp_path / "top")
70+
build_dir = os.path.join(top, "build")
71+
os.makedirs(build_dir)
72+
# Create a Makefile
73+
with open(os.path.join(build_dir, "Makefile"), "w") as f:
74+
f.write("")
75+
b = Builder(prefix, top)
76+
assert b.is_make_generator() is True
77+
78+
def test_false_when_no_makefile(self, tmp_path):
79+
prefix = MockPrefix(str(tmp_path))
80+
top = str(tmp_path / "top")
81+
os.makedirs(top)
82+
b = Builder(prefix, top)
83+
assert b.is_make_generator() is False
84+
85+
86+
# ── show_log / show_logs ─────────────────────────────────────────────────────
87+
88+
class TestShowLogs:
89+
def test_show_log_verbose_with_file(self, tmp_path, capsys):
90+
prefix = MockPrefix(str(tmp_path), verbose=True)
91+
top = str(tmp_path / "top")
92+
os.makedirs(top)
93+
b = Builder(prefix, top)
94+
log_file = os.path.join(top, "test.log")
95+
with open(log_file, "w") as f:
96+
f.write("log content\n")
97+
b.show_log(log_file)
98+
captured = capsys.readouterr()
99+
assert "log content" in captured.out
100+
101+
def test_show_log_not_verbose(self, tmp_path, capsys):
102+
prefix = MockPrefix(str(tmp_path), verbose=False)
103+
top = str(tmp_path / "top")
104+
os.makedirs(top)
105+
b = Builder(prefix, top)
106+
log_file = os.path.join(top, "test.log")
107+
with open(log_file, "w") as f:
108+
f.write("log content\n")
109+
b.show_log(log_file)
110+
captured = capsys.readouterr()
111+
assert "log content" not in captured.out
112+
113+
def test_show_log_nonexistent_file(self, tmp_path, capsys):
114+
prefix = MockPrefix(str(tmp_path), verbose=True)
115+
top = str(tmp_path / "top")
116+
os.makedirs(top)
117+
b = Builder(prefix, top)
118+
b.show_log("/nonexistent/file.log")
119+
captured = capsys.readouterr()
120+
assert captured.out == ""
121+
122+
def test_show_logs_creates_log_paths(self, tmp_path):
123+
prefix = MockPrefix(str(tmp_path), verbose=True)
124+
top = str(tmp_path / "top")
125+
os.makedirs(top)
126+
b = Builder(prefix, top)
127+
# Should not raise even with no log files
128+
b.show_logs()
129+
130+
131+
# ── targets ──────────────────────────────────────────────────────────────────
132+
133+
class TestTargets:
134+
def test_targets_empty_on_failure(self, tmp_path):
135+
prefix = MockPrefix(str(tmp_path))
136+
top = str(tmp_path / "top")
137+
os.makedirs(top)
138+
b = Builder(prefix, top)
139+
# cmake --build will fail since there's no build dir, targets should be empty
140+
result = list(b.targets())
141+
assert result == []
142+
143+
144+
# ── cmake method ─────────────────────────────────────────────────────────────
145+
146+
class TestCmake:
147+
def test_cmake_without_toolchain(self, tmp_path):
148+
prefix = MockPrefix(str(tmp_path))
149+
top = str(tmp_path / "top")
150+
os.makedirs(top)
151+
b = Builder(prefix, top)
152+
with mock.patch.object(prefix.cmd, 'cmake') as mock_cmake:
153+
b.cmake(options={'-DFOO': 'bar'})
154+
mock_cmake.assert_called_once_with(options={'-DFOO': 'bar'})
155+
156+
def test_cmake_with_toolchain(self, tmp_path):
157+
prefix = MockPrefix(str(tmp_path))
158+
top = str(tmp_path / "top")
159+
os.makedirs(top)
160+
b = Builder(prefix, top)
161+
with mock.patch.object(prefix.cmd, 'cmake') as mock_cmake:
162+
b.cmake(options={'-DFOO': 'bar'}, use_toolchain=True)
163+
call_kwargs = mock_cmake.call_args
164+
opts = call_kwargs[1]['options'] if 'options' in call_kwargs[1] else call_kwargs[0][0]
165+
assert '-DCMAKE_TOOLCHAIN_FILE' in opts
166+
167+
168+
# ── configure ────────────────────────────────────────────────────────────────
169+
170+
class TestConfigure:
171+
def test_configure_creates_build_dir(self, tmp_path):
172+
prefix = MockPrefix(str(tmp_path))
173+
top = str(tmp_path / "top")
174+
os.makedirs(top)
175+
b = Builder(prefix, top)
176+
src_dir = str(tmp_path / "src")
177+
os.makedirs(src_dir)
178+
179+
with mock.patch.object(b, 'cmake') as mock_cmake:
180+
b.configure(src_dir)
181+
assert os.path.isdir(b.build_dir)
182+
mock_cmake.assert_called_once()
183+
call_kwargs = mock_cmake.call_args
184+
args = call_kwargs[1].get('args', call_kwargs[0][0] if call_kwargs[0] else [])
185+
# Should contain src_dir
186+
assert src_dir in args
187+
188+
def test_configure_with_defines(self, tmp_path):
189+
prefix = MockPrefix(str(tmp_path))
190+
top = str(tmp_path / "top")
191+
os.makedirs(top)
192+
b = Builder(prefix, top)
193+
src_dir = str(tmp_path / "src")
194+
os.makedirs(src_dir)
195+
196+
with mock.patch.object(b, 'cmake') as mock_cmake:
197+
b.configure(src_dir, defines=["FOO=1", "BAR=2"])
198+
args = mock_cmake.call_args.kwargs['args']
199+
assert '-DFOO=1' in args
200+
assert '-DBAR=2' in args
201+
202+
def test_configure_with_generator(self, tmp_path):
203+
prefix = MockPrefix(str(tmp_path))
204+
top = str(tmp_path / "top")
205+
os.makedirs(top)
206+
b = Builder(prefix, top)
207+
src_dir = str(tmp_path / "src")
208+
os.makedirs(src_dir)
209+
210+
with mock.patch.object(b, 'cmake') as mock_cmake:
211+
b.configure(src_dir, generator="Ninja")
212+
args = mock_cmake.call_args.kwargs['args']
213+
assert '-G' in args
214+
idx = args.index('-G')
215+
assert args[idx + 1] == "Ninja"
216+
217+
def test_configure_with_install_prefix(self, tmp_path):
218+
prefix = MockPrefix(str(tmp_path))
219+
top = str(tmp_path / "top")
220+
os.makedirs(top)
221+
b = Builder(prefix, top)
222+
src_dir = str(tmp_path / "src")
223+
os.makedirs(src_dir)
224+
225+
with mock.patch.object(b, 'cmake') as mock_cmake:
226+
b.configure(src_dir, install_prefix="/install/here")
227+
args = mock_cmake.call_args.kwargs['args']
228+
assert '-DCMAKE_INSTALL_PREFIX=/install/here' in args
229+
230+
def test_configure_test_off(self, tmp_path):
231+
prefix = MockPrefix(str(tmp_path))
232+
top = str(tmp_path / "top")
233+
os.makedirs(top)
234+
b = Builder(prefix, top)
235+
src_dir = str(tmp_path / "src")
236+
os.makedirs(src_dir)
237+
238+
with mock.patch.object(b, 'cmake') as mock_cmake:
239+
b.configure(src_dir, test=False)
240+
args = mock_cmake.call_args.kwargs['args']
241+
assert '-DBUILD_TESTING=Off' in args
242+
243+
def test_configure_with_variant(self, tmp_path):
244+
prefix = MockPrefix(str(tmp_path))
245+
top = str(tmp_path / "top")
246+
os.makedirs(top)
247+
b = Builder(prefix, top)
248+
src_dir = str(tmp_path / "src")
249+
os.makedirs(src_dir)
250+
251+
with mock.patch.object(b, 'cmake') as mock_cmake:
252+
b.configure(src_dir, variant="Debug")
253+
args = mock_cmake.call_args.kwargs['args']
254+
assert '-DCMAKE_BUILD_TYPE=Debug' in args
255+
256+
257+
# ── build ────────────────────────────────────────────────────────────────────
258+
259+
class TestBuild:
260+
def test_build_basic(self, tmp_path):
261+
prefix = MockPrefix(str(tmp_path))
262+
top = str(tmp_path / "top")
263+
os.makedirs(top)
264+
b = Builder(prefix, top)
265+
266+
with mock.patch.object(b, 'cmake') as mock_cmake:
267+
b.build()
268+
args = mock_cmake.call_args.kwargs['args']
269+
assert '--build' in args
270+
assert b.build_dir in args
271+
272+
def test_build_with_target(self, tmp_path):
273+
prefix = MockPrefix(str(tmp_path))
274+
top = str(tmp_path / "top")
275+
os.makedirs(top)
276+
b = Builder(prefix, top)
277+
278+
with mock.patch.object(b, 'cmake') as mock_cmake:
279+
b.build(target="install")
280+
args = mock_cmake.call_args.kwargs['args']
281+
assert '--target' in args
282+
assert 'install' in args
283+
284+
def test_build_with_variant(self, tmp_path):
285+
prefix = MockPrefix(str(tmp_path))
286+
top = str(tmp_path / "top")
287+
os.makedirs(top)
288+
b = Builder(prefix, top)
289+
290+
with mock.patch.object(b, 'cmake') as mock_cmake:
291+
b.build(variant="Debug")
292+
args = mock_cmake.call_args.kwargs['args']
293+
assert '--config' in args
294+
assert 'Debug' in args
295+
296+
def test_build_with_makefile_adds_parallel(self, tmp_path):
297+
prefix = MockPrefix(str(tmp_path))
298+
top = str(tmp_path / "top")
299+
build_dir = os.path.join(top, "build")
300+
os.makedirs(build_dir)
301+
with open(os.path.join(build_dir, "Makefile"), "w") as f:
302+
f.write("")
303+
b = Builder(prefix, top)
304+
305+
with mock.patch.object(b, 'cmake') as mock_cmake:
306+
b.build()
307+
args = mock_cmake.call_args.kwargs['args']
308+
assert '-j' in args
309+
310+
311+
# ── test method ──────────────────────────────────────────────────────────────
312+
313+
class TestBuilderTest:
314+
def test_test_with_check_target(self, tmp_path):
315+
prefix = MockPrefix(str(tmp_path))
316+
top = str(tmp_path / "top")
317+
os.makedirs(top)
318+
b = Builder(prefix, top)
319+
320+
with mock.patch.object(b, 'targets', return_value=iter(['check'])):
321+
with mock.patch.object(b, 'build') as mock_build:
322+
b.test(variant='Release')
323+
mock_build.assert_called_once_with(target='check', variant='Release')
324+
325+
def test_test_without_check_target(self, tmp_path):
326+
prefix = MockPrefix(str(tmp_path))
327+
top = str(tmp_path / "top")
328+
os.makedirs(top)
329+
b = Builder(prefix, top)
330+
331+
with mock.patch.object(b, 'targets', return_value=iter([])):
332+
with mock.patch.object(prefix.cmd, 'ctest') as mock_ctest:
333+
b.test(variant='Release')
334+
mock_ctest.assert_called_once()

0 commit comments

Comments
 (0)