223 lines
7.0 KiB
Python
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
|