tests/cmd/external.py: use mock packages (#50363)

Signed-off-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
Co-authored-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
This commit is contained in:
Tamara Dahlgren 2025-05-15 07:59:35 -07:00 committed by GitHub
parent f1ba23316b
commit 0d586695a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 91 additions and 21 deletions

View File

@ -18,6 +18,8 @@
from spack.main import SpackCommand
from spack.spec import Spec
pytestmark = [pytest.mark.usefixtures("mock_packages")]
@pytest.fixture
def executables_found(monkeypatch):
@ -67,13 +69,24 @@ def test_get_executables(working_env, mock_executable):
# TODO: this test should be made to work, but in the meantime it is
# causing intermittent (spurious) CI failures on all PRs
@pytest.mark.not_on_windows("Test fails intermittently on Windows")
def test_find_external_cmd_not_buildable(mutable_config, working_env, mock_executable):
def test_find_external_cmd_not_buildable(
mutable_config, working_env, mock_executable, monkeypatch
):
"""When the user invokes 'spack external find --not-buildable', the config
for any package where Spack finds an external version should be marked as
not buildable.
"""
cmake_path1 = mock_executable("cmake", output="echo cmake version 1.foo")
os.environ["PATH"] = os.pathsep.join([os.path.dirname(cmake_path1)])
version = "1.foo"
@classmethod
def _determine_version(cls, exe):
return version
cmake_cls = spack.repo.PATH.get_pkg_class("cmake")
monkeypatch.setattr(cmake_cls, "determine_version", _determine_version)
cmake_path = mock_executable("cmake", output=f"echo cmake version {version}")
os.environ["PATH"] = str(cmake_path.parent)
external("find", "--not-buildable", "cmake")
pkgs_cfg = spack.config.get("packages")
assert "cmake" in pkgs_cfg
@ -89,10 +102,12 @@ def test_find_external_cmd_not_buildable(mutable_config, working_env, mock_execu
["detectable"],
[],
[
"builtin.mock.cmake",
"builtin.mock.find-externals1",
"builtin.mock.gcc",
"builtin.mock.llvm",
"builtin.mock.intel-oneapi-compilers",
"builtin.mock.llvm",
"builtin.mock.mpich",
],
),
# find --all --exclude find-externals1
@ -100,26 +115,38 @@ def test_find_external_cmd_not_buildable(mutable_config, working_env, mock_execu
None,
["detectable"],
["builtin.mock.find-externals1"],
["builtin.mock.gcc", "builtin.mock.llvm", "builtin.mock.intel-oneapi-compilers"],
[
"builtin.mock.cmake",
"builtin.mock.gcc",
"builtin.mock.intel-oneapi-compilers",
"builtin.mock.llvm",
"builtin.mock.mpich",
],
),
(
None,
["detectable"],
["find-externals1"],
["builtin.mock.gcc", "builtin.mock.llvm", "builtin.mock.intel-oneapi-compilers"],
[
"builtin.mock.cmake",
"builtin.mock.gcc",
"builtin.mock.intel-oneapi-compilers",
"builtin.mock.llvm",
"builtin.mock.mpich",
],
),
# find cmake (and cmake is not detectable)
(["cmake"], ["detectable"], [], []),
# find hwloc (and mock hwloc is not detectable)
(["hwloc"], ["detectable"], [], []),
],
)
def test_package_selection(names, tags, exclude, expected, mutable_mock_repo):
def test_package_selection(names, tags, exclude, expected):
"""Tests various cases of selecting packages"""
# In the mock repo we only have 'find-externals1' that is detectable
result = spack.cmd.external.packages_to_search_for(names=names, tags=tags, exclude=exclude)
assert set(result) == set(expected)
def test_find_external_no_manifest(mutable_config, working_env, mutable_mock_repo, monkeypatch):
def test_find_external_no_manifest(mutable_config, working_env, monkeypatch):
"""The user runs 'spack external find'; the default path for storing
manifest files does not exist. Ensure that the command does not
fail.
@ -132,7 +159,7 @@ def test_find_external_no_manifest(mutable_config, working_env, mutable_mock_rep
def test_find_external_empty_default_manifest_dir(
mutable_config, working_env, mutable_mock_repo, tmpdir, monkeypatch
mutable_config, working_env, tmpdir, monkeypatch
):
"""The user runs 'spack external find'; the default path for storing
manifest files exists but is empty. Ensure that the command does not
@ -147,7 +174,7 @@ def test_find_external_empty_default_manifest_dir(
@pytest.mark.not_on_windows("Can't chmod on Windows")
@pytest.mark.skipif(getuid() == 0, reason="user is root")
def test_find_external_manifest_with_bad_permissions(
mutable_config, working_env, mutable_mock_repo, tmpdir, monkeypatch
mutable_config, working_env, tmpdir, monkeypatch
):
"""The user runs 'spack external find'; the default path for storing
manifest files exists but with insufficient permissions. Check that
@ -167,7 +194,7 @@ def test_find_external_manifest_with_bad_permissions(
os.chmod(test_manifest_file_path, 0o700)
def test_find_external_manifest_failure(mutable_config, mutable_mock_repo, tmpdir, monkeypatch):
def test_find_external_manifest_failure(mutable_config, tmpdir, monkeypatch):
"""The user runs 'spack external find'; the manifest parsing fails with
some exception. Ensure that the command still succeeds (i.e. moves on
to other external detection mechanisms).
@ -187,7 +214,7 @@ def fail():
assert "Skipping manifest and continuing" in output
def test_find_external_merge(mutable_config, mutable_mock_repo, tmp_path):
def test_find_external_merge(mutable_config, tmp_path):
"""Checks that 'spack find external' doesn't overwrite an existing spec in packages.yaml."""
pkgs_cfg_init = {
"find-externals1": {
@ -213,7 +240,7 @@ def test_find_external_merge(mutable_config, mutable_mock_repo, tmp_path):
assert {"spec": "find-externals1@1.2", "prefix": "/x/y2"} in pkg_externals
def test_list_detectable_packages(mutable_config, mutable_mock_repo):
def test_list_detectable_packages(mutable_config):
external("list")
assert external.returncode == 0
@ -259,13 +286,23 @@ def test_new_entries_are_reported_correctly(mock_executable, mutable_config, mon
@pytest.mark.parametrize("command_args", [("-t", "build-tools"), ("-t", "build-tools", "cmake")])
@pytest.mark.not_on_windows("the test uses bash scripts")
def test_use_tags_for_detection(command_args, mock_executable, mutable_config, monkeypatch):
versions = {"cmake": "3.19.1", "openssl": "2.8.3"}
@classmethod
def _determine_version(cls, exe):
return versions[os.path.basename(exe)]
cmake_cls = spack.repo.PATH.get_pkg_class("cmake")
monkeypatch.setattr(cmake_cls, "determine_version", _determine_version)
# Prepare an environment to detect a fake cmake
cmake_exe = mock_executable("cmake", output="echo cmake version 3.19.1")
cmake_exe = mock_executable("cmake", output=f"echo cmake version {versions['cmake']}")
prefix = os.path.dirname(cmake_exe)
monkeypatch.setenv("PATH", prefix)
openssl_exe = mock_executable("openssl", output="OpenSSL 2.8.3")
openssl_exe = mock_executable("openssl", output=f"OpenSSL {versions['openssl']}")
prefix = os.path.dirname(openssl_exe)
monkeypatch.setenv("PATH", prefix)
@ -282,6 +319,16 @@ def test_failures_in_scanning_do_not_result_in_an_error(
mock_executable, monkeypatch, mutable_config
):
"""Tests that scanning paths with wrong permissions, won't cause `external find` to error."""
versions = {"first": "3.19.1", "second": "3.23.3"}
@classmethod
def _determine_version(cls, exe):
bin_parent = os.path.dirname(exe).split(os.sep)[-2]
return versions[bin_parent]
cmake_cls = spack.repo.PATH.get_pkg_class("cmake")
monkeypatch.setattr(cmake_cls, "determine_version", _determine_version)
cmake_exe1 = mock_executable(
"cmake", output="echo cmake version 3.19.1", subdir=("first", "bin")
)
@ -299,21 +346,30 @@ def test_failures_in_scanning_do_not_result_in_an_error(
assert external.returncode == 0
assert "The following specs have been" in output
assert "cmake" in output
assert "3.23.3" in output
assert "3.19.1" not in output
for vers in versions.values():
assert vers in output
def test_detect_virtuals(mock_executable, mutable_config, monkeypatch):
"""Test whether external find --not-buildable sets virtuals as non-buildable (unless user
config sets them to buildable)"""
mpich = mock_executable("mpichversion", output="echo MPICH Version: 4.0.2")
version = "4.0.2"
@classmethod
def _determine_version(cls, exe):
return version
cmake_cls = spack.repo.PATH.get_pkg_class("mpich")
monkeypatch.setattr(cmake_cls, "determine_version", _determine_version)
mpich = mock_executable("mpichversion", output=f"echo MPICH Version: {version}")
prefix = os.path.dirname(mpich)
external("find", "--path", prefix, "--not-buildable", "mpich")
# Check that mpich was correctly detected
mpich = mutable_config.get("packages:mpich")
assert mpich["buildable"] is False
assert Spec(mpich["externals"][0]["spec"]).satisfies("mpich@4.0.2")
assert Spec(mpich["externals"][0]["spec"]).satisfies(f"mpich@{version}")
# Check that the virtual package mpi was marked as non-buildable
assert mutable_config.get("packages:mpi:buildable") is False

View File

@ -21,6 +21,7 @@ class Cmake(Package):
url = "https://cmake.org/files/v3.4/cmake-3.4.3.tar.gz"
tags = ["build-tools"]
executables = ["^cmake[0-9]*$"]
depends_on("c", type="build")
depends_on("cxx", type="build")
@ -36,6 +37,12 @@ class Cmake(Package):
url="https://cmake.org/files/v3.4/cmake-3.4.3.tar.gz",
)
@classmethod
def determine_version(cls, exe):
output = Executable(exe)("--version", output=str, error=str)
match = re.search(r"cmake.*version\s+(\S+)", output)
return match.group(1) if match else None
def setup_build_environment(self, env: EnvironmentModifications) -> None:
spack_cc # Ensure spack module-scope variable is avaiable
env.set("for_install", "for_install")

View File

@ -12,6 +12,7 @@ class Mpich(Package):
list_depth = 2
tags = ["tag1", "tag2"]
executables = ["^mpichversion$"]
variant("debug", default=False, description="Compile MPICH with debug flags.")
@ -30,6 +31,12 @@ class Mpich(Package):
depends_on("cxx", type="build")
depends_on("fortran", type="build")
@classmethod
def determine_version(cls, exe):
output = Executable(exe)(output=str, error=str)
match = re.search(r"MPICH Version:\s+(\S+)", output)
return match.group(1) if match else None
def install(self, spec, prefix):
touch(prefix.mpich)