diff options
author | Adam J. Stewart <ajstewart426@gmail.com> | 2020-01-05 23:35:23 -0800 |
---|---|---|
committer | Todd Gamblin <tgamblin@llnl.gov> | 2020-01-22 21:31:12 -0800 |
commit | 11f2b612612748ee57728693c7745e3af92e9d54 (patch) | |
tree | 656c2ba1de69064c62af1fbd4f43469f7c36cac9 /share/spack/qa | |
parent | 8011fedd9ca47578e8da37a9060407c6784d7015 (diff) | |
download | spack-11f2b612612748ee57728693c7745e3af92e9d54.tar.gz spack-11f2b612612748ee57728693c7745e3af92e9d54.tar.bz2 spack-11f2b612612748ee57728693c7745e3af92e9d54.tar.xz spack-11f2b612612748ee57728693c7745e3af92e9d54.zip |
Use `spack commands --format=bash` to generate shell completion (#14393)
This PR adds a `--format=bash` option to `spack commands` to
auto-generate the Bash programmable tab completion script. It can be
extended to work for other shells.
Progress:
- [x] Fix bug in superclass initialization in `ArgparseWriter`
- [x] Refactor `ArgparseWriter` (see below)
- [x] Ensure that output of old `--format` options remains the same
- [x] Add `ArgparseCompletionWriter` and `BashCompletionWriter`
- [x] Add `--aliases` option to add command aliases
- [x] Standardize positional argument names
- [x] Tests for `spack commands --format=bash` coverage
- [x] Tests to make sure `spack-completion.bash` stays up-to-date
- [x] Tests for `spack-completion.bash` coverage
- [x] Speed up `spack-completion.bash` by caching subroutine calls
This PR also necessitates a significant refactoring of
`ArgparseWriter`. Previously, `ArgparseWriter` was mostly a single
`_write` method which handled everything from extracting the information
we care about from the parser to formatting the output. Now, `_write`
only handles recursion, while the information extraction is split into a
separate `parse` method, and the formatting is handled by `format`. This
allows subclasses to completely redefine how the format will appear
without overriding all of `_write`.
Co-Authored-by: Todd Gamblin <tgamblin@llnl.gov>
Diffstat (limited to 'share/spack/qa')
-rwxr-xr-x | share/spack/qa/completion-test.sh | 89 | ||||
-rwxr-xr-x | share/spack/qa/run-unit-tests | 17 | ||||
-rwxr-xr-x | share/spack/qa/setup-env-test.sh | 195 | ||||
-rwxr-xr-x | share/spack/qa/test-framework.sh | 195 | ||||
-rwxr-xr-x | share/spack/qa/update-completion-scripts.sh | 23 |
5 files changed, 329 insertions, 190 deletions
diff --git a/share/spack/qa/completion-test.sh b/share/spack/qa/completion-test.sh new file mode 100755 index 0000000000..5b326b4a6d --- /dev/null +++ b/share/spack/qa/completion-test.sh @@ -0,0 +1,89 @@ +#!/bin/sh +# +# Copyright 2013-2020 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) + +# +# This script tests that Spack's tab completion scripts work. +# +# The tests are portable to bash, zsh, and bourne shell, and can be run +# in any of these shells. +# + +export QA_DIR=$(dirname "$0") +export SHARE_DIR=$(cd "$QA_DIR/.." && pwd) +export SPACK_ROOT=$(cd "$QA_DIR/../../.." && pwd) + +. "$QA_DIR/test-framework.sh" + +# Fail on undefined variables +set -u + +# Source setup-env.sh before tests +. "$SHARE_DIR/setup-env.sh" +. "$SHARE_DIR/spack-completion.$_sp_shell" + +title "Testing spack-completion.$_sp_shell with $_sp_shell" + +# Spack command is now available +succeeds which spack + +title 'Testing all subcommands' +while IFS= read -r line +do + # Test that completion with no args works + succeeds _spack_completions ${line[*]} '' + + # Test that completion with flags works + contains '-h --help' _spack_completions ${line[*]} - +done <<- EOF + $(spack commands --aliases --format=subcommands) +EOF + +title 'Testing for correct output' +contains 'compiler' _spack_completions spack '' +contains 'install' _spack_completions spack inst +contains 'find' _spack_completions spack help '' +contains 'hdf5' _spack_completions spack list '' +contains 'py-numpy' _spack_completions spack list py- +contains 'mpi' _spack_completions spack providers '' +contains 'builtin' _spack_completions spack repo remove '' +contains 'packages' _spack_completions spack config edit '' +contains 'python' _spack_completions spack extensions '' +contains 'hdf5' _spack_completions spack -d install --jobs 8 '' +contains 'hdf5' _spack_completions spack install -v '' + +# XFAIL: Fails for Python 2.6 because pkg_resources not found? +#contains 'compilers.py' _spack_completions spack test '' + +title 'Testing debugging functions' + +# This is a particularly tricky case that involves the following situation: +# `spack -d [] install ` +# Here, [] represents the cursor, which is in the middle of the line. +# We should tab-complete optional flags for `spack`, not optional flags for +# `spack install` or package names. +COMP_LINE='spack -d install ' +COMP_POINT=9 +COMP_WORDS=(spack -d install) +COMP_CWORD=2 +COMP_KEY=9 +COMP_TYPE=64 + +_bash_completion_spack +contains "--all-help" echo "${COMPREPLY[@]}" + +contains "['spack', '-d', 'install', '']" _pretty_print COMP_WORDS[@] + +# Set the rest of the intermediate variables manually +COMP_WORDS_NO_FLAGS=(spack install) +COMP_CWORD_NO_FLAGS=1 +subfunction=_spack +cur= + +list_options=true +contains "'True'" _test_vars +list_options=false +contains "'False'" _test_vars diff --git a/share/spack/qa/run-unit-tests b/share/spack/qa/run-unit-tests index 8ba6eed350..52748dacdf 100755 --- a/share/spack/qa/run-unit-tests +++ b/share/spack/qa/run-unit-tests @@ -23,7 +23,7 @@ ORIGINAL_PATH="$PATH" . "$(dirname $0)/setup.sh" -check_dependencies ${coverage} git hg svn +check_dependencies $coverage git hg svn # Move to root directory of Spack # Allows script to be run from anywhere @@ -46,7 +46,7 @@ extra_args="" if [[ -n "$@" ]]; then extra_args="-k $@" fi -${coverage_run} bin/spack test -x --verbose "$extra_args" +$coverage_run bin/spack test -x --verbose "$extra_args" #----------------------------------------------------------- # Run tests for setup-env.sh @@ -57,15 +57,18 @@ export PATH="$ORIGINAL_PATH" unset spack # start in the spack root directory -cd $SPACK_ROOT +cd "$SPACK_ROOT" # Run bash tests with coverage enabled, but pipe output to /dev/null # because it seems that kcov seems to undo the script's redirection if [ "$BASH_COVERAGE" = true ]; then - ${QA_DIR}/bashcov ${QA_DIR}/setup-env-test.sh &> /dev/null + "$QA_DIR/bashcov" "$QA_DIR/setup-env-test.sh" &> /dev/null + "$QA_DIR/bashcov" "$QA_DIR/completion-test.sh" &> /dev/null fi # run the test scripts for their output (these will print nicely) -bash ${QA_DIR}/setup-env-test.sh -zsh ${QA_DIR}/setup-env-test.sh -dash ${QA_DIR}/setup-env-test.sh +bash "$QA_DIR/setup-env-test.sh" +zsh "$QA_DIR/setup-env-test.sh" +dash "$QA_DIR/setup-env-test.sh" + +bash "$QA_DIR/completion-test.sh" diff --git a/share/spack/qa/setup-env-test.sh b/share/spack/qa/setup-env-test.sh index 7613637984..66284d1a96 100755 --- a/share/spack/qa/setup-env-test.sh +++ b/share/spack/qa/setup-env-test.sh @@ -12,159 +12,11 @@ # in any of these shells. # -# ------------------------------------------------------------------------ -# Functions for color output. -# ------------------------------------------------------------------------ - -# Colors for output -red='\033[1;31m' -cyan='\033[1;36m' -green='\033[1;32m' -reset='\033[0m' - -echo_red() { - printf "${red}$*${reset}\n" -} - -echo_green() { - printf "${green}$*${reset}\n" -} - -echo_msg() { - printf "${cyan}$*${reset}\n" -} - -# ------------------------------------------------------------------------ -# Generic functions for testing shell code. -# ------------------------------------------------------------------------ - -# counts of test successes and failures. -success=0 -errors=0 - -# Print out a header for a group of tests. -title() { - echo - echo_msg "$@" - echo_msg "---------------------------------" -} - -# echo FAIL in red text; increment failures -fail() { - echo_red FAIL - errors=$((errors+1)) -} - -# -# Echo SUCCESS in green; increment successes -# -pass() { - echo_green SUCCESS - success=$((success+1)) -} - -# -# Run a command and suppress output unless it fails. -# On failure, echo the exit code and output. -# -succeeds() { - printf "'%s' succeeds ... " "$*" - output=$($* 2>&1) - err="$?" - - if [ "$err" != 0 ]; then - fail - echo_red "Command failed with error $err." - if [ -n "$output" ]; then - echo_msg "Output:" - echo "$output" - else - echo_msg "No output." - fi - else - pass - fi -} - -# -# Run a command and suppress output unless it succeeds. -# If the command succeeds, echo the output. -# -fails() { - printf "'%s' fails ... " "$*" - output=$("$@" 2>&1) - err="$?" - - if [ "$err" = 0 ]; then - fail - echo_red "Command failed with error $err." - if [ -n "$output" ]; then - echo_msg "Output:" - echo "$output" - else - echo_msg "No output." - fi - else - pass - fi -} - -# -# Ensure that a string is in the output of a command. -# Suppresses output on success. -# On failure, echo the exit code and output. -# -contains() { - string="$1" - shift - - printf "'%s' output contains '$string' ... " "$*" - output=$("$@" 2>&1) - err="$?" - - if [ "${output#*$string}" = "${output}" ]; then - fail - echo_red "Command exited with error $err." - echo_red "'$string' was not in output." - if [ -n "$output" ]; then - echo_msg "Output:" - echo "$output" - else - echo_msg "No output." - fi - else - pass - fi -} - -# -# Ensure that a variable is set. -# -is_set() { - printf "'%s' is set ... " "$1" - if eval "[ -z \${${1:-}+x} ]"; then - fail - echo_msg "$1 was not set!" - else - pass - fi -} - -# -# Ensure that a variable is not set. -# Fails and prints the value of the variable if it is set. -# -is_not_set() { - printf "'%s' is not set ... " "$1" - if eval "[ ! -z \${${1:-}+x} ]"; then - fail - echo_msg "$1 was set:" - echo " $1" - else - pass - fi -} +export QA_DIR=$(dirname "$0") +export SHARE_DIR=$(cd "$QA_DIR/.." && pwd) +export SPACK_ROOT=$(cd "$QA_DIR/../../.." && pwd) +. "$QA_DIR/test-framework.sh" # ----------------------------------------------------------------------- # Instead of invoking the module commands, we print the @@ -184,28 +36,28 @@ module() { # Make sure no environment is active unset SPACK_ENV -# fail on undefined variables +# Fail on undefined variables set -u # Source setup-env.sh before tests -. share/spack/setup-env.sh +. "$SHARE_DIR/setup-env.sh" -# bash should expand aliases even when non-interactive +# Bash should expand aliases even when non-interactive if [ -n "${BASH:-}" ]; then shopt -s expand_aliases fi title "Testing setup-env.sh with $_sp_shell" -# spack command is now avaialble +# Spack command is now available succeeds which spack -# mock cd command (intentionally define only AFTER setup-env.sh) +# Mock cd command (intentionally define only AFTER setup-env.sh) cd() { echo cd "$@" } -# create a fake mock package install and store its location for later +# Create a fake mock package install and store its location for later title "Setup" echo "Creating a mock package installation" spack -m install --fake a @@ -215,19 +67,13 @@ a_module=$(spack -m module tcl find a) b_install=$(spack location -i b) b_module=$(spack -m module tcl find b) -# create a test environment for tesitng environment commands +# Create a test environment for testing environment commands echo "Creating a mock environment" spack env create spack_test_env test_env_location=$(spack location -e spack_test_env) -# ensure that we uninstall b on exit +# Ensure that we uninstall b on exit cleanup() { - if [ "$?" != 0 ]; then - trapped_error=true - else - trapped_error=false - fi - echo "Removing test environment before exiting." spack env deactivate 2>&1 > /dev/null spack env rm -y spack_test_env @@ -235,24 +81,7 @@ cleanup() { title "Cleanup" echo "Removing test packages before exiting." spack -m uninstall -yf b a - - echo - echo "$success tests succeeded." - echo "$errors tests failed." - - if [ "$trapped_error" = true ]; then - echo "Exited due to an error." - fi - - if [ "$errors" = 0 ] && [ "$trapped_error" = false ]; then - pass - exit 0 - else - fail - exit 1 - fi } -trap cleanup EXIT # ----------------------------------------------------------------------- # Test all spack commands with special env support diff --git a/share/spack/qa/test-framework.sh b/share/spack/qa/test-framework.sh new file mode 100755 index 0000000000..14b58bbecf --- /dev/null +++ b/share/spack/qa/test-framework.sh @@ -0,0 +1,195 @@ +# Copyright 2013-2020 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) + +# +# A testing framework for any POSIX-compatible shell. +# + +# ------------------------------------------------------------------------ +# Functions for color output. +# ------------------------------------------------------------------------ + +# Colors for output +red='\033[1;31m' +cyan='\033[1;36m' +green='\033[1;32m' +reset='\033[0m' + +echo_red() { + printf "${red}$*${reset}\n" +} + +echo_green() { + printf "${green}$*${reset}\n" +} + +echo_msg() { + printf "${cyan}$*${reset}\n" +} + +# ------------------------------------------------------------------------ +# Generic functions for testing shell code. +# ------------------------------------------------------------------------ + +# counts of test successes and failures. +success=0 +errors=0 + +# Print out a header for a group of tests. +title() { + echo + echo_msg "$@" + echo_msg "---------------------------------" +} + +# echo FAIL in red text; increment failures +fail() { + echo_red FAIL + errors=$((errors+1)) +} + +# +# Echo SUCCESS in green; increment successes +# +pass() { + echo_green SUCCESS + success=$((success+1)) +} + +# +# Run a command and suppress output unless it fails. +# On failure, echo the exit code and output. +# +succeeds() { + printf "'%s' succeeds ... " "$*" + output=$("$@" 2>&1) + err="$?" + + if [ "$err" != 0 ]; then + fail + echo_red "Command failed with error $err." + if [ -n "$output" ]; then + echo_msg "Output:" + echo "$output" + else + echo_msg "No output." + fi + else + pass + fi +} + +# +# Run a command and suppress output unless it succeeds. +# If the command succeeds, echo the output. +# +fails() { + printf "'%s' fails ... " "$*" + output=$("$@" 2>&1) + err="$?" + + if [ "$err" = 0 ]; then + fail + echo_red "Command failed with error $err." + if [ -n "$output" ]; then + echo_msg "Output:" + echo "$output" + else + echo_msg "No output." + fi + else + pass + fi +} + +# +# Ensure that a string is in the output of a command. +# Suppresses output on success. +# On failure, echo the exit code and output. +# +contains() { + string="$1" + shift + + printf "'%s' output contains '$string' ... " "$*" + output=$("$@" 2>&1) + err="$?" + + if [ "${output#*$string}" = "${output}" ]; then + fail + echo_red "Command exited with error $err." + echo_red "'$string' was not in output." + if [ -n "$output" ]; then + echo_msg "Output:" + echo "$output" + else + echo_msg "No output." + fi + else + pass + fi +} + +# +# Ensure that a variable is set. +# +is_set() { + printf "'%s' is set ... " "$1" + if eval "[ -z \${${1:-}+x} ]"; then + fail + echo_msg "$1 was not set!" + else + pass + fi +} + +# +# Ensure that a variable is not set. +# Fails and prints the value of the variable if it is set. +# +is_not_set() { + printf "'%s' is not set ... " "$1" + if eval "[ ! -z \${${1:-}+x} ]"; then + fail + echo_msg "$1 was set:" + echo " $1" + else + pass + fi +} + +# +# Report the number of tests that succeeded and failed on exit. +# +teardown() { + if [ "$?" != 0 ]; then + trapped_error=true + else + trapped_error=false + fi + + if type cleanup &> /dev/null + then + cleanup + fi + + echo + echo "$success tests succeeded." + echo "$errors tests failed." + + if [ "$trapped_error" = true ]; then + echo "Exited due to an error." + fi + + if [ "$errors" = 0 ] && [ "$trapped_error" = false ]; then + pass + exit 0 + else + fail + exit 1 + fi +} + +trap teardown EXIT diff --git a/share/spack/qa/update-completion-scripts.sh b/share/spack/qa/update-completion-scripts.sh new file mode 100755 index 0000000000..8fcd321457 --- /dev/null +++ b/share/spack/qa/update-completion-scripts.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# +# Copyright 2013-2020 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) + +# Updates Spack's shell tab completion scripts + +# Switch to parent directory +QA_DIR="$(dirname "${BASH_SOURCE[0]}")" +cd "$QA_DIR/.." + +# Update each shell +for shell in bash # zsh fish +do + header=$shell/spack-completion.in + script=spack-completion.$shell + + rm -f $script + spack commands --aliases --format=$shell --header=$header --update=$script + chmod +x $script +done |