Skip to content

Commit 64ec1d4

Browse files
mattgodboltclaude
andauthored
Add comprehensive test suites for library builder classes (#1833)
Co-authored-by: Claude <[email protected]>
1 parent 6d9dccd commit 64ec1d4

File tree

3 files changed

+969
-3
lines changed

3 files changed

+969
-3
lines changed
Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
from __future__ import annotations
2+
3+
from logging import Logger
4+
from subprocess import TimeoutExpired
5+
from unittest import mock
6+
from unittest.mock import patch
7+
8+
from lib.fortran_library_builder import BuildStatus, FortranLibraryBuilder
9+
from lib.installation_context import InstallationContext
10+
from lib.library_build_config import LibraryBuildConfig
11+
12+
BASE = "https://raw.githubusercontent.com/compiler-explorer/compiler-explorer/main/etc/config/"
13+
14+
15+
def create_fortran_test_build_config():
16+
"""Create a properly configured LibraryBuildConfig mock for Fortran testing."""
17+
config = mock.Mock(spec=LibraryBuildConfig)
18+
config.lib_type = "static"
19+
config.staticliblink = ["fortranlib"]
20+
config.sharedliblink = []
21+
config.description = "Fortran test library"
22+
config.url = "https://fortran.test.url"
23+
config.build_type = "fpm"
24+
config.build_fixed_arch = ""
25+
config.build_fixed_stdlib = ""
26+
config.package_install = False
27+
config.copy_files = []
28+
config.prebuild_script = ["echo 'fortran prebuild'"]
29+
config.postbuild_script = ["echo 'fortran postbuild'"]
30+
config.configure_flags = []
31+
config.extra_cmake_arg = []
32+
config.extra_make_arg = []
33+
config.make_targets = []
34+
config.make_utility = "make"
35+
config.skip_compilers = []
36+
config.use_compiler = ""
37+
return config
38+
39+
40+
def test_get_toolchain_path_from_options_gcc_toolchain(requests_mock):
41+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
42+
logger = mock.Mock(spec_set=Logger)
43+
install_context = mock.Mock(spec_set=InstallationContext)
44+
build_config = create_fortran_test_build_config()
45+
builder = FortranLibraryBuilder(
46+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
47+
)
48+
49+
options = "--gcc-toolchain=/opt/gfortran-11 -O2"
50+
result = builder.getToolchainPathFromOptions(options)
51+
assert result == "/opt/gfortran-11"
52+
53+
54+
def test_get_toolchain_path_from_options_gxx_name(requests_mock):
55+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
56+
logger = mock.Mock(spec_set=Logger)
57+
install_context = mock.Mock(spec_set=InstallationContext)
58+
build_config = create_fortran_test_build_config()
59+
builder = FortranLibraryBuilder(
60+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
61+
)
62+
63+
options = "--gxx-name=/opt/gcc/bin/g++ -std=f2008"
64+
result = builder.getToolchainPathFromOptions(options)
65+
assert result == "/opt/gcc"
66+
67+
68+
def test_get_toolchain_path_from_options_none(requests_mock):
69+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
70+
logger = mock.Mock(spec_set=Logger)
71+
install_context = mock.Mock(spec_set=InstallationContext)
72+
build_config = create_fortran_test_build_config()
73+
builder = FortranLibraryBuilder(
74+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
75+
)
76+
77+
options = "-O2 -std=f2008"
78+
result = builder.getToolchainPathFromOptions(options)
79+
assert result is False
80+
81+
82+
def test_get_std_ver_from_options(requests_mock):
83+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
84+
logger = mock.Mock(spec_set=Logger)
85+
install_context = mock.Mock(spec_set=InstallationContext)
86+
build_config = create_fortran_test_build_config()
87+
builder = FortranLibraryBuilder(
88+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
89+
)
90+
91+
options = "-std=f2018 -O2"
92+
result = builder.getStdVerFromOptions(options)
93+
assert result == "f2018"
94+
95+
96+
def test_get_std_lib_from_options(requests_mock):
97+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
98+
logger = mock.Mock(spec_set=Logger)
99+
install_context = mock.Mock(spec_set=InstallationContext)
100+
build_config = create_fortran_test_build_config()
101+
builder = FortranLibraryBuilder(
102+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
103+
)
104+
105+
options = "-stdlib=libgfortran -O2"
106+
result = builder.getStdLibFromOptions(options)
107+
assert result == "libgfortran"
108+
109+
110+
def test_get_target_from_options(requests_mock):
111+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
112+
logger = mock.Mock(spec_set=Logger)
113+
install_context = mock.Mock(spec_set=InstallationContext)
114+
build_config = create_fortran_test_build_config()
115+
builder = FortranLibraryBuilder(
116+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
117+
)
118+
119+
options = "-target aarch64-linux-gnu -O2"
120+
result = builder.getTargetFromOptions(options)
121+
assert result == "aarch64-linux-gnu"
122+
123+
124+
def test_replace_optional_arg_with_value(requests_mock):
125+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
126+
logger = mock.Mock(spec_set=Logger)
127+
install_context = mock.Mock(spec_set=InstallationContext)
128+
build_config = create_fortran_test_build_config()
129+
builder = FortranLibraryBuilder(
130+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
131+
)
132+
133+
arg = "fpm build --arch=%arch% --build=%buildtype%"
134+
result = builder.replace_optional_arg(arg, "arch", "x86_64")
135+
assert result == "fpm build --arch=x86_64 --build=%buildtype%"
136+
137+
138+
def test_replace_optional_arg_no_value(requests_mock):
139+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
140+
logger = mock.Mock(spec_set=Logger)
141+
install_context = mock.Mock(spec_set=InstallationContext)
142+
build_config = create_fortran_test_build_config()
143+
builder = FortranLibraryBuilder(
144+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
145+
)
146+
147+
arg = "fpm build %arch?% --build=%buildtype%"
148+
result = builder.replace_optional_arg(arg, "arch", "")
149+
assert not result
150+
151+
152+
def test_expand_make_arg(requests_mock):
153+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
154+
logger = mock.Mock(spec_set=Logger)
155+
install_context = mock.Mock(spec_set=InstallationContext)
156+
build_config = create_fortran_test_build_config()
157+
builder = FortranLibraryBuilder(
158+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
159+
)
160+
161+
arg = "--arch=%arch% --build=%buildtype% --std=%stdver%"
162+
result = builder.expand_make_arg(arg, "fortran", "Debug", "x86_64", "f2018", "libgfortran")
163+
assert result == "--arch=x86_64 --build=Debug --std=f2018"
164+
165+
166+
@patch("subprocess.check_output")
167+
def test_get_conan_hash_success(mock_subprocess, requests_mock):
168+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
169+
logger = mock.Mock(spec_set=Logger)
170+
install_context = mock.Mock()
171+
install_context.dry_run = False
172+
build_config = create_fortran_test_build_config()
173+
builder = FortranLibraryBuilder(
174+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
175+
)
176+
177+
mock_subprocess.return_value = b"conanfile.py: ID: fortran123456\nOther output"
178+
builder.current_buildparameters = ["-s", "os=Linux"]
179+
180+
result = builder.get_conan_hash("/tmp/buildfolder")
181+
182+
assert result == "fortran123456"
183+
mock_subprocess.assert_called_once_with(
184+
["conan", "info", "-r", "ceserver", "."] + builder.current_buildparameters, cwd="/tmp/buildfolder"
185+
)
186+
187+
188+
@patch("subprocess.call")
189+
def test_execute_build_script_success(mock_subprocess, requests_mock):
190+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
191+
logger = mock.Mock(spec_set=Logger)
192+
install_context = mock.Mock(spec_set=InstallationContext)
193+
build_config = create_fortran_test_build_config()
194+
builder = FortranLibraryBuilder(
195+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
196+
)
197+
198+
mock_subprocess.return_value = 0
199+
200+
result = builder.executebuildscript("/tmp/buildfolder")
201+
202+
assert result == BuildStatus.Ok
203+
mock_subprocess.assert_called_once_with(["./cebuild.sh"], cwd="/tmp/buildfolder", timeout=600)
204+
205+
206+
@patch("subprocess.call")
207+
def test_execute_build_script_timeout(mock_subprocess, requests_mock):
208+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
209+
logger = mock.Mock(spec_set=Logger)
210+
install_context = mock.Mock(spec_set=InstallationContext)
211+
build_config = create_fortran_test_build_config()
212+
builder = FortranLibraryBuilder(
213+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
214+
)
215+
216+
mock_subprocess.side_effect = TimeoutExpired("cmd", 600)
217+
218+
result = builder.executebuildscript("/tmp/buildfolder")
219+
220+
assert result == BuildStatus.TimedOut
221+
222+
223+
@patch("lib.fortran_library_builder.get_ssm_param")
224+
def test_conanproxy_login_success(mock_get_ssm, requests_mock):
225+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
226+
logger = mock.Mock(spec_set=Logger)
227+
install_context = mock.Mock(spec_set=InstallationContext)
228+
build_config = create_fortran_test_build_config()
229+
builder = FortranLibraryBuilder(
230+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
231+
)
232+
233+
mock_get_ssm.return_value = "fortran_password"
234+
235+
mock_response = mock.Mock()
236+
mock_response.ok = True
237+
mock_response.content = b'{"token": "fortran_token"}'
238+
239+
with patch.object(builder.http_session, "post", return_value=mock_response):
240+
builder.conanproxy_login()
241+
242+
assert builder.conanserverproxy_token == "fortran_token"
243+
mock_get_ssm.assert_called_once_with("/compiler-explorer/conanpwd")
244+
245+
246+
def test_makebuildhash(requests_mock):
247+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
248+
logger = mock.Mock(spec_set=Logger)
249+
install_context = mock.Mock(spec_set=InstallationContext)
250+
build_config = create_fortran_test_build_config()
251+
builder = FortranLibraryBuilder(
252+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
253+
)
254+
255+
result = builder.makebuildhash(
256+
"gfortran13", "-O2", "/opt/gcc", "Linux", "Debug", "x86_64", "f2018", "libgfortran", ["flag1", "flag2"]
257+
)
258+
259+
assert result.startswith("gfortran13_")
260+
assert len(result) > len("gfortran13_")
261+
262+
263+
def test_does_compiler_support_fixed_target_match(requests_mock):
264+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
265+
logger = mock.Mock(spec_set=Logger)
266+
install_context = mock.Mock(spec_set=InstallationContext)
267+
build_config = create_fortran_test_build_config()
268+
builder = FortranLibraryBuilder(
269+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
270+
)
271+
272+
result = builder.does_compiler_support(
273+
"/usr/bin/gfortran", "fortran", "x86_64-linux-gnu", "-target x86_64-linux-gnu", ""
274+
)
275+
assert result is True
276+
277+
278+
def test_does_compiler_support_fixed_target_mismatch(requests_mock):
279+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
280+
logger = mock.Mock(spec_set=Logger)
281+
install_context = mock.Mock(spec_set=InstallationContext)
282+
build_config = create_fortran_test_build_config()
283+
builder = FortranLibraryBuilder(
284+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
285+
)
286+
287+
result = builder.does_compiler_support("/usr/bin/gfortran", "fortran", "x86", "-target x86_64-linux-gnu", "")
288+
assert result is False
289+
290+
291+
def test_get_commit_hash_with_git(requests_mock):
292+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
293+
logger = mock.Mock(spec_set=Logger)
294+
install_context = mock.Mock(spec_set=InstallationContext)
295+
build_config = create_fortran_test_build_config()
296+
builder = FortranLibraryBuilder(
297+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
298+
)
299+
300+
with patch("os.path.exists", return_value=True):
301+
with patch("subprocess.check_output") as mock_subprocess:
302+
mock_subprocess.return_value = b"abc1234 Latest fortran commit\n"
303+
304+
result = builder.get_commit_hash()
305+
306+
assert result == "abc1234"
307+
308+
309+
def test_get_commit_hash_without_git(requests_mock):
310+
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
311+
logger = mock.Mock(spec_set=Logger)
312+
install_context = mock.Mock(spec_set=InstallationContext)
313+
build_config = create_fortran_test_build_config()
314+
builder = FortranLibraryBuilder(
315+
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
316+
)
317+
318+
with patch("os.path.exists", return_value=False):
319+
result = builder.get_commit_hash()
320+
assert result == "2.0.0" # target_name

0 commit comments

Comments
 (0)