volume_key/tests/utils/rlwrap.sh

509 lines
12 KiB
Bash
Raw Normal View History

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# File: ./tests/utils/rlwrap.sh
# Author: Jiri Kucera <jkucera@redhat.com>
# Brief: Wrapper around beakerlib (rlX) functions
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# Copyright (c) 2020 Red Hat, Inc.
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see http://www.gnu.org/licenses/.
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
##
# Result, ResultA, ResultB
#
# If a function has an output, it is stored inside these variables.
Result=""
ResultA=""
ResultB=""
# Internal variables used by RunCmdViaExpect family of functions.
_rlwrap_expect_script_path=""
declare -ag _rlwrap_rlRun_options=()
declare -ag _rlwrap_expect_options=()
_rlwrap_expect_script=""
_rlwrap_expect_script_scommand=""
declare -ag _rlwrap_expect_script_command_args=()
declare -ag _rlwrap_expect_script_input_args=()
_rlwrap_rlRun_status="0"
_rlwrap_rlRun_comment=""
# Internal variables used by RunTest family of functions.
declare -ag _CleanupActions=()
declare -ag _Tests=()
##
# errmsg ERRMSG
#
# ERRMSG
# error message
#
# Print ERRMSG to standard error output.
function errmsg() {
echo "$1" >&2
}
##
# required_options OPTION_LIST
#
# OPTION_LIST
# a list of option names (without --)
#
# For every X from OPTION_LIST, test if --$X was specified by checking that
# __${X//-/_} is not empty.
function required_options() {
local __optvar=""
while [[ $# -gt 0 ]]; do
__optvar="__${1//-/_}"
if [[ -z "${!__optvar}" ]]; then
errmsg "${FUNCTION[1]}: Missing required option: --$1"
return 1
fi
shift
done
}
##
# invalid_argument ARG_NAME
#
# ARG_NAME
# argument name
#
# Report to standard error output that ARG_NAME is not a valid argument and
# return 1.
function invalid_argument() {
errmsg "${FUNCTION[1]}: Invalid argument '$1'."
return 1
}
##
# Concat ARGS
#
# ARGS
# list of arguments
#
# Make a string by concatenating all arguments from ARGS. Useful for creating
# long comments for rlRun.
function Concat() {
echo "$*"
}
##
# RunCmd COMMAND [COMMAND_ARGS]
#
# COMMAND
# command that should be run
# COMMAND_ARGS
# command arguments
#
# Shorthand for RunCmdX -- COMMAND COMMAND_ARGS.
function RunCmd() {
RunCmdX -- "$@"
}
##
# RunCmdX [-t] [-l] [-c] [-s] [STATUS] [COMMENT] [--] COMMAND [COMMAND_ARGS]
#
# -t, -l, -c, -s
# see rlRun
# STATUS
# see rlRun
# COMMENT
# see rlRun
# --
# options-command delimiter
# COMMAND
# command that should be run
# COMMAND_ARGS
# command arguments
#
# Wrapper around beakerlib's rlRun that allows COMMAND and its arguments to be
# passed separately and not as one long string.
function RunCmdX() {
local __tflag=""
local __lflag=""
local __cflag=""
local __sflag=""
local __command=""
local __status="0"
local __comment=""
# Handle short options:
while [[ $# -gt 0 ]]; do
case "$1" in
-t) __tflag="$1" ;;
-l) __lflag="$1" ;;
-c) __cflag="$1" ;;
-s) __sflag="$1" ;;
*) break ;;
esac
shift
done
# First positional argument before -- is expected status code:
if [[ -n "${1:-}" ]] && [[ "$1" != "--" ]]; then
__status="$1"
shift
fi
# Second positional argument before -- is comment:
if [[ -n "${1:-}" ]] && [[ "$1" != "--" ]]; then
__comment="$1"
shift
fi
# Consume options-command delimiter:
if [[ "${1:-}" == "--" ]]; then
shift
fi
# Command name is required:
if [[ -z "${1:-}" ]]; then
errmsg "Expected command."
return 1
fi
__command="$1"; shift
# The rest of options are command arguments:
while [[ $# -gt 0 ]]; do
__command="${__command} $1"
shift
done
# Let the game begin:
rlRun ${__tflag} ${__lflag} ${__cflag} ${__sflag} \
"${__command}" "${__status}" "${__comment}"
}
##
# RunCmdViaExpect
#
# Starts a specification of command that should be run via expect. This is
# handy for interactive commands. General usage is
#
# RunCmdViaExpect
# Path ${ScriptsDir}
# rlRunOptions -s
# ExpectOptions -f
# Command cryptsetup luksFormat ${VOLUME}
# Input --password ${PASSWD}
# Status 0
# Comment "Format ${VOLUME}"
# FinishRun || return $?
#
# In the example above, Path specifies the directory where the expect script
# is located. If it is omitted, EXPECT_SCRIPTS_PATH environment variable is
# read. If EXPECT_SCRIPTS_PATH is not set, `.` is used.
#
# rlRunOptions are options for rlRun, like -s and -t (see beakerlib manual).
#
# ExpectOptions are options for expect tool or Tcl interpreter, not for the
# script.
#
# Command is a command together with its arguments that will be run via expect.
# The first command argument, the command itself, is used as a name of expect
# script so in the example above the name of expect script will be
# cryptsetup.exp. This script must exist in directory specified by Path. The
# rest of Command arguments will be passed to the end of this script's command
# line and it is up to script's implementation what happen to them.
#
# Input gather arguments that specify input data that are feed to command by
# expect tool when they are asked for.
#
# Input, Command, ExpectOptions, and rlRunOptions work in accumulative way.
# That is, you can write `Command cryptsetup luksFormat ${VOLUME}` as a two
# Command calls, e.g. `Command cryptsetup` and `luksFormat ${VOLUME}`. This
# allow to split long commands accross multiple lines without using backslash
# character, which has the benefit of writing comments for particular command
# options.
#
# Status is the expected status/return code (default is 0).
#
# Comment is the comment as described in rlRun documentation. The default
# comment is a string made from arguments of Command separated by spaces.
#
# FinishRun makes a final arguments for rlRun and execute it. In our case, the
# rlRun call will look like this
#
# rlRun -s "${ScriptsDir}/cryptsetup.exp -f -- --password ${PASSWD} --
# luksFormat ${VOLUME}" 0 "Format ${VOLUME}"
#
# The return code of rlRun is the return code of FinishRun. To parse its
# command line, cryptsetup.exp uses cmdline package from tcllib.
function RunCmdViaExpect() {
_rlwrap_expect_script_path="${EXPECT_SCRIPTS_PATH:-.}"
_rlwrap_rlRun_options=()
_rlwrap_expect_options=()
_rlwrap_expect_script=""
_rlwrap_expect_script_scommand=""
_rlwrap_expect_script_command_args=()
_rlwrap_expect_script_input_args=()
_rlwrap_rlRun_status="0"
_rlwrap_rlRun_comment=""
alias Path=_rlwrap_Path
alias rlRunOptions=_rlwrap_rlRunOptions
alias ExpectOptions=_rlwrap_ExpectOptions
alias Command=_rlwrap_Command
alias Input=_rlwrap_Input
alias Status=_rlwrap_Status
alias Comment=_rlwrap_Comment
}
##
# _rlwrap_Path [PATH]
#
# PATH
# PATH to script directory
#
# See Path in RunCmdViaExpect.
function _rlwrap_Path() {
if [[ $# -gt 0 ]]; then
_rlwrap_expect_script_path="${1}"
fi
}
##
# _rlwrap_rlRunOptions [OPTIONS]
#
# OPTIONS
# options for rlRun
#
# See rlRunOptions in RunCmdViaExpect.
function _rlwrap_rlRunOptions() {
_rlwrap_rlRun_options+=( "$@" )
}
##
# _rlwrap_ExpectOptions [OPTIONS]
#
# OPTIONS
# options for expect tool
#
# See ExpectOptions in RunCmdViaExpect.
function _rlwrap_ExpectOptions() {
_rlwrap_expect_options+=( "$@" )
}
##
# _rlwrap_Command [COMMAND_OR_OPTION] [COMMAND_OPTIONS]
#
# COMMAND_OR_OPTION
# command name or option (depending on a number of Command invocations)
# COMMAND_OPTIONS
# command options
#
# See Command in RunCmdViaExpect.
function _rlwrap_Command() {
if [[ -z "${_rlwrap_expect_script}" ]]; then
if [[ -n "${1:-}" ]]; then
_rlwrap_expect_script="${1}.exp"
_rlwrap_expect_script_scommand="${1}"
shift
fi
fi
if [[ $# -gt 0 ]]; then
_rlwrap_expect_script_command_args+=( "$@" )
_rlwrap_expect_script_scommand="${_rlwrap_expect_script_scommand} $*"
fi
}
##
# _rlwrap_Input [OPTIONS]
#
# OPTIONS
# options for expect script that are used for passing input values to
# commands that are run from within the script
#
# See Input in RunCmdViaExpect.
function _rlwrap_Input() {
_rlwrap_expect_script_input_args+=( "$@" )
}
##
# _rlwrap_Status [STATUS_CODE]
#
# STATUS_CODE
# expected status/return code of expect script
#
# See Status in RunCmdViaExpect.
function _rlwrap_Status() {
if [[ $# -gt 0 ]]; then
_rlwrap_rlRun_status="${1}"
fi
}
##
# _rlwrap_Comment [COMMENT]
#
# COMMENT
# comment to be passed to rlRun
#
# See Comment in RunCmdViaExpect.
function _rlwrap_Comment() {
if [[ $# -gt 0 ]]; then
_rlwrap_rlRun_comment="${1}"
fi
}
##
# FinishRun
#
# See RunCmdViaExpect.
function FinishRun() {
local __command=""
unalias Path
unalias rlRunOptions
unalias ExpectOptions
unalias Command
unalias Input
unalias Status
unalias Comment
if [[ -z "${_rlwrap_expect_script}" ]]; then
errmsg "RunCmdViaExpect: Missing name of expect script!"
errmsg "| The name of expect script is deduced from the first"
errmsg "| argument given to Command."
return 1
fi
if [[ -z "${_rlwrap_rlRun_comment}" ]]; then
_rlwrap_rlRun_comment="${_rlwrap_expect_script_scommand}"
fi
__command="${_rlwrap_expect_script_path}/${_rlwrap_expect_script}"
__command="${__command} ${_rlwrap_expect_options[*]} --"
__command="${__command} ${_rlwrap_expect_script_input_args[*]} --"
__command="${__command} ${_rlwrap_expect_script_command_args[*]}"
rlRun "${_rlwrap_rlRun_options[@]}" "${__command}" \
"${_rlwrap_rlRun_status}" "${_rlwrap_rlRun_comment}"
}
##
# CreateTemporaryDirectory
#
# Create a temporary directory and store its path to Result.
function CreateTemporaryDirectory() {
Result="$(mktemp -d)"
}
##
# PushDir DIRECTORY
#
# DIRECTORY
# path to directory
#
# Perform `rlRun pushd DIRECTORY`.
function PushDir() {
RunCmd pushd "\"$1\""
}
##
# PopDir
#
# Perform `rlRun popd`.
function PopDir() {
RunCmd popd
}
##
# AtCleanup COMMAND
#
# COMMAND
# cleanup action as a command
#
# Insert COMMAND to the beginning of the list of cleanup actions.
function AtCleanup() {
_CleanupActions=( "$1" "${_CleanupActions[@]}" )
}
##
# AddTest TESTFUNC [DESCRIPTION]
#
# TESTFUNC
# function that performs the test
# DESCRIPTION
# test description
#
# Add test to the list of tests.
function AddTest() {
_Tests+=( "$1=${2:-}" )
}
##
# DoSetup
#
# Invoke Setup function and return its status code. Setup must be defined
# before.
function DoSetup() {
local __status=0
rlPhaseStartSetup
if [[ "$(LC_ALL=C type -t Setup)" != "function" ]]; then
rlFail "Function 'Setup' is not defined. Please, define it."
else
Setup
fi
__status=$?
rlPhaseEnd
return ${__status}
}
##
# DoTests
#
# Run all tests from the tests list.
function DoTests() {
for __testspec in "${_Tests[@]}"; do
# __testspec has the format 'testfunc=test description':
rlPhaseStartTest "${__testspec#*=}"
"${__testspec%%=*}" || :
rlPhaseEnd
done
}
##
# DoCleanup
#
# Run all registered cleanup actions in the reverse order than they were
# registered by AtCleanup.
function DoCleanup() {
rlPhaseStartCleanup
for __action in "${_CleanupActions[@]}"; do
"${__action}" || :
done
rlPhaseEnd
}
##
# RunTest
#
# Test runner entry point. Perform setup, run all tests, and perform cleanup.
function RunTest() {
rlJournalStart
DoSetup && DoTests
DoCleanup
rlJournalPrintText
rlJournalEnd
}