# Common definitions for all test suites in test/* testdefs_sh=$(abspath "${BASH_SOURCE[0]}") servald_source_root="${testdefs_sh%/*}" servald_build_root="$servald_source_root" declare -a instance_stack=() # Some useful regular expressions. These must work in grep(1) as basic # expressions, and also in sed(1). rexp_sid='[0-9a-fA-F]\{64\}' # Utility function for creating servald fixtures: # - set $servald variable (executable under test) # - set the current instance to be "Z" setup_servald() { servald=$(abspath "$servald_build_root/dna") # The servald executable under test if ! [ -x "$servald" ]; then error "servald executable not present: $servald" return 1 fi servald_basename="${servald##*/}" set_instance +Z } # Utility function for running servald and asserting no errors: # - executes $servald with the given arguments # - asserts that standard error contains no error messages executeOk_servald() { executeOk --executable="$servald" "$@" assertStderrGrep --matches=0 --message='stderr of $executed contains no error messages' '^ERROR:' } # Utility function: # - if the argument is a prefixed instance name "+X", then call set_instance # "X" and return 0, otherwise leave the current instance unchanged and return 1 # Designed for use in functions that take an optional instance name as their # first argument: # func() { # push_instance # set_instance_fromarg "$1" && shift # ... # pop_instance # } # would be invoked as: # func +A blah blah # func +B wow wow # func foo bar set_instance_fromarg() { case "$1" in +[A-Z]) set_instance "$1"; return 0;; esac return 1 } # Utility function: # - push the current instance on the instance stack push_instance() { instance_stack+=("$instance_name") } # Utility function: # - pop an instance off the instance stack pop_instance() { local n=${#instance_stack[*]} [ $n -eq 0 ] && error "instance stack underflow" let --n unset instance_stack[$n] } # Utility function: # - create a temporary directory to contain all per-instance test files # - set SERVALINSTANCE_PATH environment variable to name a directory within # the per-instance test directory (but do not create it) # - set other environment variables to support other functions defined in this # script set_instance() { case "$1" in '') error "missing instance name argument" ;; +[A-Z]) instance_name="${1#+}" echo "# set instance = $instance_name" export instance_dir="$TFWTMP/instance/$instance_name" mkdir -p "$instance_dir" export instance_servald_log="$instance_dir/servald.log" export SERVALINSTANCE_PATH="$instance_dir/servald" export instance_servald_pidfile="$SERVALINSTANCE_PATH/servald.pid" ;; *) error "malformed instance name argument, must be in form +[A-Z]" ;; esac } # Utility function for setting up servald JNI fixtures: # - check that libservald.so is present # - set LD_LIBRARY_PATH so that libservald.so can be found setup_servald_so() { assert [ -r "$servald_build_root/libservald.so" ] export LD_LIBRARY_PATH="$servald_build_root" } # Utility function for setting up a fixture with a servald server process: # - Ensure that no servald processes are running # - Start a servald server process # - Ensure that it is still running after one second start_servald_server() { push_instance set_instance_fromarg "$1" && shift executeOk $servald config set logfile "$instance_servald_log" # Start servald server local -a before_pids local -a after_pids get_servald_pids before_pids echo "# before_pids=$before_pids" unset SERVALD_OUTPUT_DELIMITER executeOk $servald start "$@" tfw_cat --stdout get_servald_pids after_pids echo "# after_pids=$after_pids" # Assert that the servald pid file is present. assert --message="servald pidfile was created" [ -s "$instance_servald_pidfile" ] servald_pid=$(cat "$instance_servald_pidfile") assert --message="servald pidfile contains a valid pid" --dump-on-fail="$instance_servald_log" kill -0 "$servald_pid" # Assert that there is at least one new servald process running. local apid bpid local new_pids= local pidfile_running=false for apid in ${after_pids[*]}; do local isnew=true for bpid in ${before_pids[*]}; do if [ "$apid" -eq "$bpid" ]; then isnew=false break fi done if [ "$apid" -eq "$servald_pid" ]; then echo "# started servald process: pid=$servald_pid" new_pids="$new_pids $apid" pidfile_running=true elif $isnew; then echo "# unknown new servald process: pid=$apid" new_pids="$new_pids $apid" fi done assert --message="a new servald process is running" --dump-on-fail="$instance_servald_log" [ -n "$new_pids" ] assert --message="servald pidfile process is running" --dump-on-fail="$instance_servald_log" $pidfile_running echo "# Started servald server process $instance_name, pid=$servald_pid" pop_instance } # Utility function: # - stop a servald server process instance in an orderly fashion # - cat its log file into the test log stop_servald_server() { push_instance set_instance_fromarg "$1" && shift # Stop servald server servald_pid=$(cat "$instance_servald_pidfile") local -a before_pids local -a after_pids get_servald_pids before_pids echo "# before_pids=$before_pids" unset SERVALD_OUTPUT_DELIMITER executeOk $servald stop "$@" tfw_cat --stdout echo "# Stopped servald server process $instance_name, pid=${servald_pid:-unknown}" get_servald_pids after_pids echo "# after_pids=$after_pids" # Assert that the servald pid file is gone. assert --message="servald pidfile was removed" [ ! -e "$instance_servald_pidfile" ] # Assert that the servald process identified by the pidfile is no longer running. local apid bpid if [ -n "$servald_pid" ]; then for apid in ${after_pids[*]}; do assert --message="servald process still running" [ "$apid" -ne "$servald_pid" ] done fi # Append the server log file to the test log. [ -s "$instance_servald_log" ] && tfw_cat "$instance_servald_log" # Check there is at least one fewer servald processes running. for bpid in ${before_pids[*]}; do local isgone=true for apid in ${after_pids[*]}; do if [ "$apid" -eq "$bpid" ]; then isgone=false break fi done if $isgone; then echo "# ended servald process: pid=$bpid" fi done pop_instance } # Utility function for tearing down servald fixtures: # - stop all servald server process instances in an orderly fashion stop_all_servald_servers() { push_instance if pushd "$TFWTMP/instance" >/dev/null; then for name in *; do set_instance "+$name" stop_servald_server done popd >/dev/null fi pop_instance } # Utility function for tearing down servald fixtures: # - send a given signal to all running servald processes, identified by name # - return 1 if no processes were present, 0 if any signal was sent signal_all_servald_processes() { local sig="$1" local servald_pids get_servald_pids servald_pids local pid local ret=1 for pid in $servald_pids; do if kill -$sig "$pid"; then echo "# Sent SIG$sig to servald process pid=$pid" ret=0 else echo "# servald process pid=$pid not running -- SIG$sig not sent" fi done return $ret } # Utility function for tearing down servald fixtures: # - wait while any servald processes remain # - return 0 if no processes are present # - 1 if the timeout elapses first wait_all_servald_processes() { local timeout="${1:-1000000}" sleep $timeout & sleep_pid=$! while get_servald_pids; do kill -0 $sleep_pid 2>/dev/null || return 1 sleep 0.1 _tfw_echo -n . done kill -TERM $sleep_pid 2>/dev/null return 0 } # Utility function for tearing down servald fixtures: # - terminate all running servald processes, identified by name, by sending # two SIGHUPs 100ms apart, then another SIGHUP after 2 seconds, finally # SIGKILL after 2 seconds # - return 0 if no more processes are running, nonzero otherwise kill_all_servald_processes() { for delay in 0.1 2 2; do signal_all_servald_processes HUP || return 0 wait_all_servald_processes $delay && return 0 done signal_all_servald_processes KILL || return 0 return 1 } # Utility function: # - return the PIDs of all servald processes the current user is running in the # named array variable (if no name given, do not set any variable) # - return 0 if there are any servald processes running, 1 if not get_servald_pids() { local var="$1" if [ -z "$servald" ]; then error "\$servald is not set" return 1 fi local mypid=$$ # XXX The following line will not find any PIDs if there are spaces in "$servald". local pids=$(ps -u$UID -o pid,args | awk -v mypid="$mypid" -v servald="$servald" '$1 != mypid && $2 == servald {print $1}') [ -n "$var" ] && eval "$var=($pids)" [ -n "$pids" ] } # Assertion function: # - assert there are no existing servald server processes assert_no_servald_processes() { local pids get_servald_pids pids assert --message="$servald_basename process(es) running: $pids" [ -z "$pids" ] return 0 } # Assertion function: # - assert the given instance's servald server log contains no errors assert_servald_server_no_errors() { assertStderrGrep --matches=0 --message='stderr of $executed contains no error messages' '^ERROR:' }