tests and completions for spack find --json and spack find --format

This commit is contained in:
Todd Gamblin 2019-09-02 10:18:14 -07:00
parent 2dc7467760
commit 1b877e8e0f
5 changed files with 217 additions and 19 deletions

View File

@ -221,8 +221,8 @@ def display_specs_as_json(specs, deps=False):
sjson.dump(records, sys.stdout)
def iter_sections(specs, indent, all_headers):
"""Break a list of specs into sections indexed by arch/compiler."""
def iter_groups(specs, indent, all_headers):
"""Break a list of specs into groups indexed by arch/compiler."""
# Make a dict with specs keyed by architecture and compiler.
index = index_by(specs, ('architecture', 'compiler'))
ispace = indent * ' '
@ -278,9 +278,9 @@ def display_specs(specs, args=None, **kwargs):
show_flags (bool): Show compiler flags with specs
variants (bool): Show variants with specs
indent (int): indent each line this much
sections (bool): display specs grouped by arch/compiler (default True)
groups (bool): display specs grouped by arch/compiler (default True)
decorators (dict): dictionary mappng specs to decorators
header_callback (function): called at start of arch/compiler sections
header_callback (function): called at start of arch/compiler groups
all_headers (bool): show headers even when arch/compiler aren't defined
"""
@ -300,7 +300,7 @@ def get_arg(name, default=None):
flags = get_arg('show_flags', False)
full_compiler = get_arg('show_full_compiler', False)
variants = get_arg('variants', False)
sections = get_arg('sections', True)
groups = get_arg('groups', True)
all_headers = get_arg('all_headers', False)
decorator = get_arg('decorator', None)
@ -338,7 +338,7 @@ def fmt(s, depth=0):
return string
def format_list(specs):
"""Display a single list of specs, with no sections"""
"""Display a single list of specs, with no groups"""
# create the final, formatted versions of all specs
formatted = []
for spec in specs:
@ -367,8 +367,8 @@ def format_list(specs):
else:
print(string)
if sections:
for specs in iter_sections(specs, indent, all_headers):
if groups:
for specs in iter_groups(specs, indent, all_headers):
format_list(specs)
else:
format_list(sorted(specs))

View File

@ -35,10 +35,10 @@ def setup_parser(subparser):
subparser.add_argument('-p', '--paths', action='store_true',
help='show paths to package install directories')
subparser.add_argument(
'--sections', action='store_true', default=None, dest='sections',
help='group specs in arch/compiler sections (default on)')
'--groups', action='store_true', default=None, dest='groups',
help='display specs in arch/compiler groups (default on)')
subparser.add_argument(
'--no-sections', action='store_false', default=None, dest='sections',
'--no-groups', action='store_false', default=None, dest='groups',
help='do not group specs by arch/compiler')
arguments.add_common_arguments(
@ -177,16 +177,16 @@ def find(parser, args):
if env:
decorator, added, roots, removed = setup_env(env)
# use sections by default except with format.
if args.sections is None:
args.sections = not args.format
# use groups by default except with format.
if args.groups is None:
args.groups = not args.format
# Exit early if no package matches the constraint
# Exit early with an error code if no package matches the constraint
if not results and args.constraint:
msg = "No package matches the query: {0}"
msg = msg.format(' '.join(args.constraint))
tty.msg(msg)
return
return 1
# If tags have been specified on the command line, filter by tags
if args.tags:
@ -199,7 +199,7 @@ def find(parser, args):
else:
if env:
display_env(env, args, decorator)
if args.sections:
if args.groups:
tty.msg("%s" % plural(len(results), 'installed package'))
cmd.display_specs(
results, args, decorator=decorator, all_headers=True)

View File

@ -4,15 +4,20 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import argparse
import json
import pytest
import spack.cmd as cmd
import spack.cmd.find
from spack.main import SpackCommand
from spack.spec import Spec
from spack.util.pattern import Bunch
find = SpackCommand('find')
base32_alphabet = 'abcdefghijklmnopqrstuvwxyz234567'
@pytest.fixture(scope='module')
def parser():
@ -111,3 +116,181 @@ def test_namespaces_shown_correctly(database):
out = find('--namespace')
assert 'builtin.mock.zmpi' in out
def _check_json_output(spec_list):
assert len(spec_list) == 3
assert all(spec["name"] == "mpileaks" for spec in spec_list)
deps = [spec["dependencies"] for spec in spec_list]
assert sum(["zmpi" in d for d in deps]) == 1
assert sum(["mpich" in d for d in deps]) == 1
assert sum(["mpich2" in d for d in deps]) == 1
def _check_json_output_deps(spec_list):
assert len(spec_list) == 13
names = [spec["name"] for spec in spec_list]
assert names.count("mpileaks") == 3
assert names.count("callpath") == 3
assert names.count("zmpi") == 1
assert names.count("mpich") == 1
assert names.count("mpich2") == 1
assert names.count("fake") == 1
assert names.count("dyninst") == 1
assert names.count("libdwarf") == 1
assert names.count("libelf") == 1
@pytest.mark.db
def test_find_json(database):
output = find('--json', 'mpileaks')
spec_list = json.loads(output)
_check_json_output(spec_list)
@pytest.mark.db
def test_find_json_deps(database):
output = find('-d', '--json', 'mpileaks')
spec_list = json.loads(output)
_check_json_output_deps(spec_list)
@pytest.mark.db
def test_display_json(database, capsys):
specs = [Spec(s).concretized() for s in [
"mpileaks ^zmpi",
"mpileaks ^mpich",
"mpileaks ^mpich2",
]]
cmd.display_specs_as_json(specs)
spec_list = json.loads(capsys.readouterr()[0])
_check_json_output(spec_list)
cmd.display_specs_as_json(specs + specs + specs)
spec_list = json.loads(capsys.readouterr()[0])
_check_json_output(spec_list)
@pytest.mark.db
def test_display_json_deps(database, capsys):
specs = [Spec(s).concretized() for s in [
"mpileaks ^zmpi",
"mpileaks ^mpich",
"mpileaks ^mpich2",
]]
cmd.display_specs_as_json(specs, deps=True)
spec_list = json.loads(capsys.readouterr()[0])
_check_json_output_deps(spec_list)
cmd.display_specs_as_json(specs + specs + specs, deps=True)
spec_list = json.loads(capsys.readouterr()[0])
_check_json_output_deps(spec_list)
@pytest.mark.db
def test_find_format(database, config):
output = find('--format', '{name}-{^mpi.name}', 'mpileaks')
assert set(output.strip().split('\n')) == set([
"mpileaks-zmpi",
"mpileaks-mpich",
"mpileaks-mpich2",
])
output = find('--format', '{name}-{version}-{compiler.name}-{^mpi.name}',
'mpileaks')
assert set(output.strip().split('\n')) == set([
"mpileaks-2.3-gcc-zmpi",
"mpileaks-2.3-gcc-mpich",
"mpileaks-2.3-gcc-mpich2",
])
output = find('--format', '{name}-{^mpi.name}-{hash:7}',
'mpileaks')
elements = output.strip().split('\n')
assert set(e[:-7] for e in elements) == set([
"mpileaks-zmpi-",
"mpileaks-mpich-",
"mpileaks-mpich2-",
])
# hashes are in base32
for e in elements:
for c in e[-7:]:
assert c in base32_alphabet
@pytest.mark.db
def test_find_format_deps(database, config):
output = find('-d', '--format', '{name}-{version}', 'mpileaks', '^zmpi')
assert output == """\
mpileaks-2.3
callpath-1.0
dyninst-8.2
libdwarf-20130729
libelf-0.8.13
zmpi-1.0
fake-1.0
"""
@pytest.mark.db
def test_find_format_deps_paths(database, config):
output = find('-dp', '--format', '{name}-{version}', 'mpileaks', '^zmpi')
spec = Spec("mpileaks ^zmpi").concretized()
prefixes = [s.prefix for s in spec.traverse()]
assert output == """\
mpileaks-2.3 {0}
callpath-1.0 {1}
dyninst-8.2 {2}
libdwarf-20130729 {3}
libelf-0.8.13 {4}
zmpi-1.0 {5}
fake-1.0 {6}
""".format(*prefixes)
@pytest.mark.db
def test_find_very_long(database, config):
output = find('-L', '--no-groups', "mpileaks")
specs = [Spec(s).concretized() for s in [
"mpileaks ^zmpi",
"mpileaks ^mpich",
"mpileaks ^mpich2",
]]
assert set(output.strip().split("\n")) == set([
("%s mpileaks@2.3" % s.dag_hash()) for s in specs
])
@pytest.mark.db
def test_find_show_compiler(database, config):
output = find('--no-groups', '--show-full-compiler', "mpileaks")
assert "mpileaks@2.3%gcc@4.5.0" in output
@pytest.mark.db
def test_find_not_found(database, config, capsys):
with capsys.disabled():
output = find("foobarbaz", fail_on_error=False)
assert "No package matches the query: foobarbaz" in output
assert find.returncode == 1
@pytest.mark.db
def test_find_no_sections(database, config):
output = find()
assert "-----------" in output
output = find("--no-groups")
assert "-----------" not in output
assert "==>" not in output

View File

@ -104,6 +104,20 @@ def descend_and_check(iterable, level=0):
assert level >= 5
def test_to_record_dict(mock_packages, config):
specs = ['mpileaks', 'zmpi', 'dttop']
for name in specs:
spec = Spec(name).concretized()
record = spec.to_record_dict()
assert record["name"] == name
assert "hash" in record
node = spec.to_node_dict()
for key, value in node[name].items():
assert key in record
assert record[key] == value
def test_ordered_read_not_required_for_consistent_dag_hash(
config, mock_packages
):

View File

@ -521,7 +521,8 @@ function _spack_fetch {
function _spack_find {
if $list_options
then
compgen -W "-h --help -s --short -p --paths -d --deps -l --long
compgen -W "-h --help -s --short -d --deps -p --paths
--format --json --groups --no-groups -l --long
-L --very-long -t --tags -c --show-concretized
-f --show-flags --show-full-compiler -x --explicit
-X --implicit -u --unknown -m --missing -v --variants
@ -1282,7 +1283,7 @@ function _all_resource_hashes {
}
function _installed_packages {
spack --color=never find | grep -v "^--"
spack --color=never find --no-groups
}
function _installed_compilers {