File indexing completed on 2026-09-05 08:17:12
0001
0002 """Self-tests for select_lockfile.py.
0003
0004 Guards asset-name parsing and the selection rules. Picking the wrong lockfile
0005 does not fail here: it fails minutes later in a configure step, on a runner.
0006
0007 Run directly; non-zero exit on failure. Also collectable by pytest.
0008 """
0009
0010 from __future__ import annotations
0011
0012 import sys
0013 from pathlib import Path
0014 from typing import Dict, Optional
0015
0016 HERE = Path(__file__).resolve().parent
0017 sys.path.insert(0, str(HERE))
0018
0019 import select_lockfile as sl
0020
0021
0022
0023 V24_ASSETS = [
0024 "spack_darwin-tahoe-aarch64.lock",
0025 "spack_darwin-tahoe-aarch64_apple-clang@17.0.0_cxx23.lock",
0026 "spack_linux-almalinux9-x86_64.lock",
0027 "spack_linux-almalinux9-x86_64_gcc@15.2.1_cxx23.lock",
0028 "spack_linux-ubuntu26.04-aarch64_gcc@15.2.0_cxx23.lock",
0029 "spack_linux-ubuntu26.04-x86_64.lock",
0030 "spack_linux-ubuntu26.04-x86_64_gcc@15.2.0_cxx20.lock",
0031 "spack_linux-ubuntu26.04-x86_64_gcc@15.2.0_cxx23.lock",
0032 "spack_linux-ubuntu26.04-x86_64_gcc@15.2.0_cxx23_cuda13.lock",
0033 "spack_linux-ubuntu26.04-x86_64_gcc@15.2.0_cxx23_rocm-gfx90a.lock",
0034 "spack_linux-ubuntu26.04-x86_64_llvm@22.1.8_cxx23.lock",
0035 ]
0036
0037
0038 V23_ASSETS = [
0039 "spack_linux-ubuntu24.04-x86_64.lock",
0040 "spack_linux-ubuntu24.04-x86_64_gcc@13.3.0_cxx20.lock",
0041 "spack_linux-ubuntu24.04-x86_64_llvm@22.1.7_cxx20.lock",
0042 "spack_linux-ubuntu24.04-x86_64_llvm@22.1.7_cxx23.lock",
0043 ]
0044
0045 UBUNTU26 = "linux-ubuntu26.04-x86_64"
0046 UBUNTU24 = "linux-ubuntu24.04-x86_64"
0047
0048
0049 def _release(names: list[str]) -> Dict:
0050 return {
0051 "assets": [
0052 {"name": n, "browser_download_url": f"https://example.invalid/{n}"}
0053 for n in names
0054 ]
0055 }
0056
0057
0058 def _select(
0059 names: list[str],
0060 arch: str,
0061 compiler: Optional[str],
0062 cxx: str = "cxx20",
0063 flavor: Optional[str] = None,
0064 ) -> str:
0065 """Select against a synthetic release, returning the chosen asset name."""
0066 lockfiles = sl.parse_assets(_release(names))
0067 url = sl.select_lockfile(lockfiles, arch, compiler, cxx, flavor)
0068 return url.rsplit("/", 1)[-1]
0069
0070
0071
0072
0073
0074 def test_extract_flavor_host_specs():
0075 assert sl.extract_flavor("gcc@15.2.0_cxx23") is None
0076 assert sl.extract_flavor("gcc@13.3.0") is None, "pre-cxx spec has no flavor"
0077 assert sl.extract_flavor("default") is None
0078
0079
0080 def test_extract_flavor_accelerator_specs():
0081 assert sl.extract_flavor("gcc@15.2.0_cxx23_cuda13") == "cuda13"
0082 assert sl.extract_flavor("gcc@15.2.0_cxx23_rocm-gfx90a") == "rocm-gfx90a"
0083
0084
0085 def test_extract_flavor_keeps_underscores_in_flavor_name():
0086
0087 assert sl.extract_flavor("gcc@15.2.0_cxx23_cuda13_sm90") == "cuda13_sm90"
0088
0089
0090 def test_normalize_flavor():
0091 assert sl.normalize_flavor("host") is None, "'host' is the plain CPU stack"
0092 assert sl.normalize_flavor("") is None
0093 assert sl.normalize_flavor(None) is None
0094 assert sl.normalize_flavor(" cuda13 ") == "cuda13"
0095
0096
0097
0098
0099
0100 def test_flavored_lockfile_is_selected():
0101 got = _select(V24_ASSETS, UBUNTU26, "gcc@15.2.0", cxx="cxx23", flavor="cuda13")
0102 assert got == "spack_linux-ubuntu26.04-x86_64_gcc@15.2.0_cxx23_cuda13.lock", got
0103
0104
0105 def test_flavor_name_with_dash_is_selected():
0106 got = _select(V24_ASSETS, UBUNTU26, "gcc@15.2.0", cxx="cxx23", flavor="rocm-gfx90a")
0107 assert (
0108 got == "spack_linux-ubuntu26.04-x86_64_gcc@15.2.0_cxx23_rocm-gfx90a.lock"
0109 ), got
0110
0111
0112 def test_host_request_never_picks_a_flavored_lockfile():
0113
0114 for cxx in ("cxx20", "cxx23"):
0115 got = _select(V24_ASSETS, UBUNTU26, "gcc@15.2.0", cxx=cxx)
0116 assert sl.extract_flavor(got) is None, f"{cxx} picked flavored {got}"
0117
0118
0119 def test_flavor_falls_back_across_cxx_but_not_across_flavor():
0120
0121
0122 got = _select(V24_ASSETS, UBUNTU26, "gcc@15.2.0", cxx="cxx20", flavor="cuda13")
0123 assert got == "spack_linux-ubuntu26.04-x86_64_gcc@15.2.0_cxx23_cuda13.lock", got
0124
0125
0126 def test_unknown_flavor_exits_rather_than_falling_back():
0127
0128
0129 try:
0130 _select(V24_ASSETS, UBUNTU26, "gcc@15.2.0", cxx="cxx23", flavor="cuda12")
0131 except SystemExit as e:
0132 assert e.code == 1, e.code
0133 else:
0134 raise AssertionError("expected a hard failure for an unavailable flavor")
0135
0136
0137 def test_flavor_request_on_arch_without_flavors_exits():
0138 try:
0139 _select(V23_ASSETS, UBUNTU24, "gcc@13.3.0", cxx="cxx20", flavor="cuda13")
0140 except SystemExit as e:
0141 assert e.code == 1, e.code
0142 else:
0143 raise AssertionError("expected a hard failure on a flavorless release")
0144
0145
0146
0147
0148
0149 def test_exact_compiler_and_cxx_match():
0150 got = _select(V24_ASSETS, UBUNTU26, "gcc@15.2.0", cxx="cxx20")
0151 assert got == "spack_linux-ubuntu26.04-x86_64_gcc@15.2.0_cxx20.lock", got
0152
0153
0154 def test_clang_is_an_alias_for_llvm():
0155 got = _select(V24_ASSETS, UBUNTU26, "clang@22.1.8", cxx="cxx23")
0156 assert got == "spack_linux-ubuntu26.04-x86_64_llvm@22.1.8_cxx23.lock", got
0157
0158
0159 def test_unknown_compiler_version_uses_highest_of_family():
0160 got = _select(V23_ASSETS, UBUNTU24, "llvm@22.1.1", cxx="cxx23")
0161 assert got == "spack_linux-ubuntu24.04-x86_64_llvm@22.1.7_cxx23.lock", got
0162
0163
0164 def test_no_compiler_uses_the_arch_default():
0165 got = _select(V24_ASSETS, UBUNTU26, None, cxx="cxx20")
0166 assert got == "spack_linux-ubuntu26.04-x86_64.lock", got
0167
0168
0169 def test_v23_host_selection_is_unchanged():
0170
0171 got = _select(V23_ASSETS, UBUNTU24, "gcc@13.3.0", cxx="cxx20")
0172 assert got == "spack_linux-ubuntu24.04-x86_64_gcc@13.3.0_cxx20.lock", got
0173
0174
0175 def _main() -> int:
0176 failures = 0
0177 for name, fn in sorted(globals().items()):
0178 if name.startswith("test_") and callable(fn):
0179 try:
0180 fn()
0181 print(f"ok {name}")
0182 except AssertionError as e:
0183 failures += 1
0184 print(f"FAIL {name}: {e}")
0185 print(f"\n{failures} failure(s)")
0186 return 1 if failures else 0
0187
0188
0189 if __name__ == "__main__":
0190 sys.exit(_main())