spack/lib/spack/spack/test/cmd/spec.py
2024-11-13 13:03:29 +01:00

223 lines
7.0 KiB
Python

# Copyright 2013-2024 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import re
import pytest
import spack.config
import spack.environment as ev
import spack.error
import spack.spec
import spack.store
from spack.main import SpackCommand, SpackCommandError
pytestmark = pytest.mark.usefixtures("mutable_config", "mutable_mock_repo")
spec = SpackCommand("spec")
def test_spec():
output = spec("mpileaks")
assert "mpileaks@2.3" in output
assert "callpath@1.0" in output
assert "dyninst@8.2" in output
assert "libdwarf@20130729" in output
assert "libelf@0.8.1" in output
assert "mpich@3.0.4" in output
def test_spec_concretizer_args(mutable_database, do_not_check_runtimes_on_reuse):
"""End-to-end test of CLI concretizer prefs.
It's here to make sure that everything works from CLI
options to `solver.py`, and that config options are not
lost along the way.
"""
# remove two non-preferred mpileaks installations
# so that reuse will pick up the zmpi one
uninstall = SpackCommand("uninstall")
uninstall("-y", "mpileaks^mpich")
uninstall("-y", "mpileaks^mpich2")
# get the hash of mpileaks^zmpi
mpileaks_zmpi = spack.store.STORE.db.query_one("mpileaks^zmpi")
h = mpileaks_zmpi.dag_hash()[:7]
output = spec("--fresh", "-l", "mpileaks")
assert h not in output
output = spec("--reuse", "-l", "mpileaks")
assert h in output
def test_spec_parse_dependency_variant_value():
"""Verify that we can provide multiple key=value variants to multiple separate
packages within a spec string."""
output = spec("multivalue-variant fee=barbaz ^ pkg-a foobar=baz")
assert "fee=barbaz" in output
assert "foobar=baz" in output
def test_spec_parse_cflags_quoting():
"""Verify that compiler flags can be provided to a spec from the command line."""
output = spec("--yaml", 'gcc cflags="-Os -pipe" cxxflags="-flto -Os"')
gh_flagged = spack.spec.Spec.from_yaml(output)
assert ["-Os", "-pipe"] == gh_flagged.compiler_flags["cflags"]
assert ["-flto", "-Os"] == gh_flagged.compiler_flags["cxxflags"]
def test_spec_yaml():
output = spec("--yaml", "mpileaks")
mpileaks = spack.spec.Spec.from_yaml(output)
assert "mpileaks" in mpileaks
assert "callpath" in mpileaks
assert "dyninst" in mpileaks
assert "libdwarf" in mpileaks
assert "libelf" in mpileaks
assert "mpich" in mpileaks
def test_spec_json():
output = spec("--json", "mpileaks")
mpileaks = spack.spec.Spec.from_json(output)
assert "mpileaks" in mpileaks
assert "callpath" in mpileaks
assert "dyninst" in mpileaks
assert "libdwarf" in mpileaks
assert "libelf" in mpileaks
assert "mpich" in mpileaks
def test_spec_format(mutable_database):
output = spec("--format", "{name}-{^mpi.name}", "mpileaks^mpich")
assert output.rstrip("\n") == "mpileaks-mpich"
def _parse_types(string):
"""Parse deptypes for specs from `spack spec -t` output."""
lines = string.strip().split("\n")
result = {}
for line in lines:
match = re.match(r"\[([^]]*)\]\s*\^?([^@]*)@", line)
if match:
types, name = match.groups()
result.setdefault(name, []).append(types)
result[name] = sorted(result[name])
return result
def test_spec_deptypes_nodes():
output = spec("--types", "--cover", "nodes", "--no-install-status", "dt-diamond")
types = _parse_types(output)
assert types["dt-diamond"] == [" "]
assert types["dt-diamond-left"] == ["bl "]
assert types["dt-diamond-right"] == ["bl "]
assert types["dt-diamond-bottom"] == ["blr "]
def test_spec_deptypes_edges():
output = spec("--types", "--cover", "edges", "--no-install-status", "dt-diamond")
types = _parse_types(output)
assert types["dt-diamond"] == [" "]
assert types["dt-diamond-left"] == ["bl "]
assert types["dt-diamond-right"] == ["bl "]
assert types["dt-diamond-bottom"] == ["b ", "blr "]
def test_spec_returncode():
with pytest.raises(SpackCommandError):
spec()
assert spec.returncode == 1
def test_spec_parse_error():
with pytest.raises(spack.error.SpecSyntaxError) as e:
spec("1.15:")
# make sure the error is formatted properly
error_msg = "unexpected tokens in the spec string\n1.15:\n ^"
assert error_msg in str(e.value)
def test_env_aware_spec(mutable_mock_env_path):
env = ev.create("test")
env.add("mpileaks")
with env:
output = spec()
assert "mpileaks@2.3" in output
assert "callpath@1.0" in output
assert "dyninst@8.2" in output
assert "libdwarf@20130729" in output
assert "libelf@0.8.1" in output
assert "mpich@3.0.4" in output
@pytest.mark.parametrize(
"name, version, error",
[
("develop-branch-version", "f3c7206350ac8ee364af687deaae5c574dcfca2c=develop", None),
("develop-branch-version", "git." + "a" * 40 + "=develop", None),
("callpath", "f3c7206350ac8ee364af687deaae5c574dcfca2c=1.0", spack.error.FetchError),
("develop-branch-version", "git.foo=0.2.15", None),
],
)
def test_spec_version_assigned_git_ref_as_version(name, version, error):
if error:
with pytest.raises(error):
output = spec(name + "@" + version)
else:
output = spec(name + "@" + version)
assert version in output
@pytest.mark.parametrize(
"unify, spec_hash_args, match, error",
[
# success cases with unfiy:true
(True, ["mpileaks_mpich"], "mpich", None),
(True, ["mpileaks_zmpi"], "zmpi", None),
(True, ["mpileaks_mpich", "dyninst"], "mpich", None),
(True, ["mpileaks_zmpi", "dyninst"], "zmpi", None),
# same success cases with unfiy:false
(False, ["mpileaks_mpich"], "mpich", None),
(False, ["mpileaks_zmpi"], "zmpi", None),
(False, ["mpileaks_mpich", "dyninst"], "mpich", None),
(False, ["mpileaks_zmpi", "dyninst"], "zmpi", None),
# cases with unfiy:false
(True, ["mpileaks_mpich", "mpileaks_zmpi"], "callpath, mpileaks", spack.error.SpecError),
(False, ["mpileaks_mpich", "mpileaks_zmpi"], "zmpi", None),
],
)
def test_spec_unification_from_cli(
install_mockery, mutable_config, mutable_database, unify, spec_hash_args, match, error
):
"""Ensure specs grouped together on the CLI are concretized together when unify:true."""
spack.config.set("concretizer:unify", unify)
db = spack.store.STORE.db
spec_lookup = {
"mpileaks_mpich": db.query_one("mpileaks ^mpich").dag_hash(),
"mpileaks_zmpi": db.query_one("mpileaks ^zmpi").dag_hash(),
"dyninst": db.query_one("dyninst").dag_hash(),
}
hashes = [f"/{spec_lookup[name]}" for name in spec_hash_args]
if error:
with pytest.raises(error, match=match):
output = spec(*hashes)
else:
output = spec(*hashes)
assert match in output