854 lines
26 KiB
Bash
854 lines
26 KiB
Bash
|
#!/bin/sh
|
||
|
|
||
|
# NUT Integration Testing suite, assumes the codebase was built and
|
||
|
# arranges running of the binaries to test driver-client-server
|
||
|
# ability to start and their interactions.
|
||
|
#
|
||
|
# Note: currently it is a PoC-quality mess that gets the job done
|
||
|
# but could be refactored for better maintainability and generic
|
||
|
# approach. Part of the goal was to let this script set up the
|
||
|
# sandbox to run tests which could be defined in other files.
|
||
|
#
|
||
|
# WARNING: Current working directory when starting the script should be
|
||
|
# the location where it may create temporary data (e.g. the BUILDDIR).
|
||
|
# Caller can export envvars to impact the script behavior, e.g.:
|
||
|
# DEBUG=true to print debug messages, running processes, etc.
|
||
|
# DEBUG_SLEEP=60 to sleep after tests, with driver+server running
|
||
|
# NUT_DEBUG_MIN=3 to set (minimum) debug level for drivers, upsd...
|
||
|
# NUT_PORT=12345 custom port for upsd to listen and clients to query
|
||
|
#
|
||
|
# Design note: written with dumbed-down POSIX shell syntax, to
|
||
|
# properly work in whatever different OSes have (bash, dash,
|
||
|
# ksh, busybox sh...)
|
||
|
#
|
||
|
# Copyright
|
||
|
# 2022 Jim Klimov <jimklimov+nut@gmail.com>
|
||
|
#
|
||
|
# License: GPLv2+
|
||
|
|
||
|
TZ=UTC
|
||
|
LANG=C
|
||
|
LC_ALL=C
|
||
|
export TZ LANG LC_ALL
|
||
|
|
||
|
log_separator() {
|
||
|
echo "" >&2
|
||
|
echo "================================" >&2
|
||
|
}
|
||
|
|
||
|
shouldDebug() {
|
||
|
[ -n "$DEBUG" ] || [ -n "$DEBUG_SLEEP" ]
|
||
|
}
|
||
|
|
||
|
log_debug() {
|
||
|
if shouldDebug ; then
|
||
|
echo "[DEBUG] $@" >&2
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
log_info() {
|
||
|
echo "[INFO] $@" >&2
|
||
|
}
|
||
|
|
||
|
log_error() {
|
||
|
echo "[ERROR] $@" >&2
|
||
|
}
|
||
|
|
||
|
die() {
|
||
|
echo "[FATAL] $@" >&2
|
||
|
exit 1
|
||
|
}
|
||
|
|
||
|
# Note: current directory is assumed to be writeable for temporary
|
||
|
# data, e.g. the $(builddir) from the Makefile. Static resources
|
||
|
# from the source codebase are where the script resides, e.g.
|
||
|
# the $(srcdir) from the Makefile. If we are not in the source
|
||
|
# tree, tests would use binaries in PATH (e.g. packaged install).
|
||
|
BUILDDIR="`pwd`"
|
||
|
TOP_BUILDDIR=""
|
||
|
case "${BUILDDIR}" in
|
||
|
*/tests/NIT)
|
||
|
TOP_BUILDDIR="`cd "${BUILDDIR}"/../.. && pwd`" ;;
|
||
|
*) log_info "Current directory '${BUILDDIR}' is not a .../tests/NIT" ;;
|
||
|
esac
|
||
|
if ! test -w "${BUILDDIR}" ; then
|
||
|
log_error "BUILDDIR='${BUILDDIR}' is not writeable, tests may fail below"
|
||
|
fi
|
||
|
|
||
|
SRCDIR="`dirname "$0"`"
|
||
|
SRCDIR="`cd "$SRCDIR" && pwd`"
|
||
|
TOP_SRCDIR=""
|
||
|
case "${SRCDIR}" in
|
||
|
*/tests/NIT)
|
||
|
TOP_SRCDIR="`cd "${SRCDIR}"/../.. && pwd`" ;;
|
||
|
*) log_info "Script source directory '${SRCDIR}' is not a .../tests/NIT" ;;
|
||
|
esac
|
||
|
|
||
|
# No fuss about LD_LIBRARY_PATH: for binaries that need it,
|
||
|
# PATH entries below would contain libtool wrapper scripts;
|
||
|
# for other builds we use system default or caller's env.
|
||
|
PATH_ADD="${BUILDDIR}"
|
||
|
if [ x"${SRCDIR}" != x"${BUILDDIR}" ]; then
|
||
|
PATH_ADD="${PATH_ADD}:${SRCDIR}"
|
||
|
fi
|
||
|
|
||
|
if [ x"${TOP_BUILDDIR}" != x ]; then
|
||
|
PATH_ADD="${PATH_ADD}:${TOP_BUILDDIR}/clients:${TOP_BUILDDIR}/drivers:${TOP_BUILDDIR}/server:${TOP_BUILDDIR}/tools:${TOP_BUILDDIR}/tools/nut-scanner"
|
||
|
fi
|
||
|
|
||
|
if [ x"${TOP_SRCDIR}" != x ]; then
|
||
|
PATH_ADD="${PATH_ADD}:${TOP_SRCDIR}/clients:${TOP_SRCDIR}/drivers:${TOP_SRCDIR}/server:${TOP_SRCDIR}/tools:${TOP_SRCDIR}/tools/nut-scanner"
|
||
|
fi
|
||
|
|
||
|
PATH="${PATH_ADD}:${PATH}"
|
||
|
export PATH
|
||
|
unset PATH_ADD
|
||
|
|
||
|
log_debug "Using PATH='$PATH'"
|
||
|
|
||
|
for PROG in upsd upsc dummy-ups upsmon ; do
|
||
|
(command -v ${PROG}) || die "Useless setup: ${PROG} not found in PATH: ${PATH}"
|
||
|
done
|
||
|
|
||
|
PID_UPSD=""
|
||
|
PID_DUMMYUPS=""
|
||
|
PID_DUMMYUPS1=""
|
||
|
PID_DUMMYUPS2=""
|
||
|
|
||
|
TESTDIR="$BUILDDIR/tmp"
|
||
|
# Technically the limit is sizeof(sockaddr.sun_path) for complete socket
|
||
|
# pathname, which varies 104-108 chars max on systems seen in CI farm;
|
||
|
# we reserve 17 chars for "/dummy-ups-dummy" longest filename.
|
||
|
if [ `echo "$TESTDIR" | wc -c` -gt 80 ]; then
|
||
|
log_info "'$TESTDIR' is too long to store AF_UNIX socket files, will mktemp"
|
||
|
if ! ( [ -n "${TMPDIR-}" ] && [ -d "${TMPDIR-}" ] && [ -w "${TMPDIR-}" ] ) ; then
|
||
|
if [ -d /dev/shm ] && [ -w /dev/shm ]; then TMPDIR=/dev/shm ; else TMPDIR=/tmp ; fi
|
||
|
fi
|
||
|
TESTDIR="`mktemp -d "${TMPDIR}/nit-tmp.$$.XXXXXX"`" || die "Failed to mktemp"
|
||
|
else
|
||
|
rm -rf "${TESTDIR}" || true
|
||
|
fi
|
||
|
log_info "Using '$TESTDIR' for generated configs and state files"
|
||
|
|
||
|
mkdir -p "${TESTDIR}/etc" "${TESTDIR}/run" && chmod 750 "${TESTDIR}/run" \
|
||
|
|| die "Failed to create temporary FS structure for the NIT"
|
||
|
|
||
|
stop_daemons() {
|
||
|
if [ -n "$PID_UPSD$PID_DUMMYUPS$PID_DUMMYUPS1$PID_DUMMYUPS2" ] ; then
|
||
|
log_info "Stopping test daemons"
|
||
|
kill -15 $PID_UPSD $PID_DUMMYUPS $PID_DUMMYUPS1 $PID_DUMMYUPS2 2>/dev/null
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
trap 'RES=$?; stop_daemons; if [ "${TESTDIR}" != "${BUILDDIR}/tmp" ] ; then rm -rf "${TESTDIR}" ; fi; exit $RES;' 0 1 2 3 15
|
||
|
|
||
|
NUT_STATEPATH="${TESTDIR}/run"
|
||
|
NUT_ALTPIDPATH="${TESTDIR}/run"
|
||
|
NUT_CONFPATH="${TESTDIR}/etc"
|
||
|
export NUT_STATEPATH NUT_ALTPIDPATH NUT_CONFPATH
|
||
|
|
||
|
# TODO: Find a portable way to (check and) grab a random unprivileged port?
|
||
|
[ -n "${NUT_PORT-}" ] && [ "$NUT_PORT" -gt 0 ] && [ "$NUT_PORT" -lt 65536 ] \
|
||
|
|| {
|
||
|
DELTA1="`date +%S`" || DELTA1=0
|
||
|
DELTA2="`expr $$ % 99`" || DELTA2=0
|
||
|
|
||
|
NUT_PORT="`expr 34931 + $DELTA1 + $DELTA2`" \
|
||
|
&& [ "$NUT_PORT" -gt 0 ] && [ "$NUT_PORT" -lt 65536 ] \
|
||
|
|| NUT_PORT=34931
|
||
|
}
|
||
|
export NUT_PORT
|
||
|
|
||
|
### upsd.conf: ##################################################
|
||
|
|
||
|
generatecfg_upsd_trivial() {
|
||
|
# Populate the configs for the run
|
||
|
cat > "$NUT_CONFPATH/upsd.conf" << EOF
|
||
|
STATEPATH "$NUT_STATEPATH"
|
||
|
LISTEN localhost $NUT_PORT
|
||
|
EOF
|
||
|
[ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: upsd.conf"
|
||
|
chmod 640 "$NUT_CONFPATH/upsd.conf"
|
||
|
|
||
|
# Some systems listining on symbolic "localhost" actually
|
||
|
# only bind to IPv6, and Python telnetlib resolves IPv4
|
||
|
# and fails its connection tests. Others fare well with
|
||
|
# both addresses in one command.
|
||
|
for LH in 127.0.0.1 '::1' ; do
|
||
|
if (
|
||
|
( cat /etc/hosts || getent hosts ) | grep "$LH" \
|
||
|
|| ping -c 1 "$LH"
|
||
|
) 2>/dev/null >/dev/null ; then
|
||
|
echo "LISTEN $LH $NUT_PORT" >> "$NUT_CONFPATH/upsd.conf"
|
||
|
fi
|
||
|
done
|
||
|
|
||
|
if [ -n "${NUT_DEBUG_MIN-}" ] ; then
|
||
|
echo "DEBUG_MIN ${NUT_DEBUG_MIN}" >> "$NUT_CONFPATH/upsd.conf" || exit
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
generatecfg_upsd_nodev() {
|
||
|
generatecfg_upsd_trivial
|
||
|
echo "ALLOW_NO_DEVICE true" >> "$NUT_CONFPATH/upsd.conf" \
|
||
|
|| die "Failed to populate temporary FS structure for the NIT: upsd.conf"
|
||
|
}
|
||
|
|
||
|
### upsd.users: ##################################################
|
||
|
|
||
|
TESTPASS_ADMIN='mypass'
|
||
|
TESTPASS_TESTER='pass words'
|
||
|
TESTPASS_UPSMON_PRIMARY='P@ssW0rdAdm'
|
||
|
TESTPASS_UPSMON_SECONDARY='P@ssW0rd'
|
||
|
|
||
|
generatecfg_upsdusers_trivial() {
|
||
|
cat > "$NUT_CONFPATH/upsd.users" << EOF
|
||
|
[admin]
|
||
|
password = $TESTPASS_ADMIN
|
||
|
actions = SET
|
||
|
instcmds = ALL
|
||
|
|
||
|
[tester]
|
||
|
password = "${TESTPASS_TESTER}"
|
||
|
instcmds = test.battery.start
|
||
|
instcmds = test.battery.stop
|
||
|
|
||
|
[dummy-admin-m]
|
||
|
password = "${TESTPASS_UPSMON_PRIMARY}"
|
||
|
upsmon master
|
||
|
|
||
|
[dummy-admin]
|
||
|
password = "${TESTPASS_UPSMON_PRIMARY}"
|
||
|
upsmon primary
|
||
|
|
||
|
[dummy-user-s]
|
||
|
password = "${TESTPASS_UPSMON_SECONDARY}"
|
||
|
upsmon slave
|
||
|
|
||
|
[dummy-user]
|
||
|
password = "${TESTPASS_UPSMON_SECONDARY}"
|
||
|
upsmon secondary
|
||
|
EOF
|
||
|
[ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: upsd.users"
|
||
|
chmod 640 "$NUT_CONFPATH/upsd.users"
|
||
|
}
|
||
|
|
||
|
### upsmon.conf: ##################################################
|
||
|
|
||
|
generatecfg_upsmon_trivial() {
|
||
|
# Populate the configs for the run
|
||
|
( echo 'MINSUPPLIES 0' > "$NUT_CONFPATH/upsmon.conf" || exit
|
||
|
echo 'SHUTDOWNCMD "echo TESTING_DUMMY_SHUTDOWN_NOW"' >> "$NUT_CONFPATH/upsmon.conf" || exit
|
||
|
|
||
|
if [ -n "${NUT_DEBUG_MIN-}" ] ; then
|
||
|
echo "DEBUG_MIN ${NUT_DEBUG_MIN}" >> "$NUT_CONFPATH/upsmon.conf" || exit
|
||
|
fi
|
||
|
) || die "Failed to populate temporary FS structure for the NIT: upsmon.conf"
|
||
|
chmod 640 "$NUT_CONFPATH/upsmon.conf"
|
||
|
}
|
||
|
|
||
|
generatecfg_upsmon_master() {
|
||
|
generatecfg_upsmon_trivial
|
||
|
echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-admin-m' '${TESTPASS_UPSMON_PRIMARY}' master" >> "$NUT_CONFPATH/upsmon.conf" \
|
||
|
|| die "Failed to populate temporary FS structure for the NIT: upsmon.conf"
|
||
|
}
|
||
|
|
||
|
generatecfg_upsmon_primary() {
|
||
|
generatecfg_upsmon_trivial
|
||
|
echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-admin' '${TESTPASS_UPSMON_PRIMARY}' primary" >> "$NUT_CONFPATH/upsmon.conf" \
|
||
|
|| die "Failed to populate temporary FS structure for the NIT: upsmon.conf"
|
||
|
}
|
||
|
|
||
|
generatecfg_upsmon_slave() {
|
||
|
generatecfg_upsmon_trivial
|
||
|
echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-user-s' '${TESTPASS_UPSMON_SECONDARY}' slave" >> "$NUT_CONFPATH/upsmon.conf" \
|
||
|
|| die "Failed to populate temporary FS structure for the NIT: upsmon.conf"
|
||
|
}
|
||
|
|
||
|
generatecfg_upsmon_secondary() {
|
||
|
generatecfg_upsmon_trivial
|
||
|
echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-user' '${TESTPASS_UPSMON_SECONDARY}' secondary" >> "$NUT_CONFPATH/upsmon.conf" \
|
||
|
|| die "Failed to populate temporary FS structure for the NIT: upsmon.conf"
|
||
|
}
|
||
|
|
||
|
### ups.conf: ##################################################
|
||
|
|
||
|
generatecfg_ups_trivial() {
|
||
|
# Populate the configs for the run
|
||
|
( echo 'maxretry = 3' > "$NUT_CONFPATH/ups.conf" || exit
|
||
|
if [ x"${TOP_BUILDDIR}" != x ]; then
|
||
|
echo "driverpath = '${TOP_BUILDDIR}/drivers'" >> "$NUT_CONFPATH/ups.conf" || exit
|
||
|
fi
|
||
|
if [ -n "${NUT_DEBUG_MIN-}" ] ; then
|
||
|
echo "debug_min = ${NUT_DEBUG_MIN}" >> "$NUT_CONFPATH/ups.conf" || exit
|
||
|
fi
|
||
|
) || die "Failed to populate temporary FS structure for the NIT: ups.conf"
|
||
|
chmod 640 "$NUT_CONFPATH/ups.conf"
|
||
|
|
||
|
}
|
||
|
|
||
|
generatecfg_ups_dummy() {
|
||
|
generatecfg_ups_trivial
|
||
|
|
||
|
cat > "$NUT_CONFPATH/dummy.seq" << EOF
|
||
|
ups.status: OB
|
||
|
TIMER 5
|
||
|
ups.status: OL
|
||
|
TIMER 5
|
||
|
EOF
|
||
|
[ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: dummy.seq"
|
||
|
|
||
|
cat >> "$NUT_CONFPATH/ups.conf" << EOF
|
||
|
[dummy]
|
||
|
driver = dummy-ups
|
||
|
desc = "Crash Dummy"
|
||
|
port = dummy.seq
|
||
|
#mode = dummy-loop
|
||
|
EOF
|
||
|
[ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: ups.conf"
|
||
|
|
||
|
if [ x"${TOP_SRCDIR}" != x ]; then
|
||
|
cp "${TOP_SRCDIR}/data/evolution500.seq" "${TOP_SRCDIR}/data/epdu-managed.dev" "$NUT_CONFPATH/"
|
||
|
|
||
|
cat >> "$NUT_CONFPATH/ups.conf" << EOF
|
||
|
[UPS1]
|
||
|
driver = dummy-ups
|
||
|
desc = "Example event sequence"
|
||
|
port = evolution500.seq
|
||
|
|
||
|
[UPS2]
|
||
|
driver = dummy-ups
|
||
|
desc = "Example ePDU data dump"
|
||
|
port = epdu-managed.dev
|
||
|
mode = dummy-once
|
||
|
EOF
|
||
|
[ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: ups.conf"
|
||
|
|
||
|
# HACK: Avoid empty ups.status that may be present in example docs
|
||
|
# FIXME: Might we actually want that value (un-)set for tests?..
|
||
|
# TODO: Check if the problem was with dummy-ups looping? [#1385]
|
||
|
for F in "$NUT_CONFPATH/"*.dev "$NUT_CONFPATH/"*.seq ; do
|
||
|
sed -e 's,^ups.status: *$,ups.status: OL BOOST,' -i'.bak' "$F"
|
||
|
grep -E '^ups.status:' "$F" >/dev/null || { echo "ups.status: OL BOOST" >> "$F"; }
|
||
|
done
|
||
|
fi
|
||
|
|
||
|
}
|
||
|
|
||
|
#####################################################
|
||
|
|
||
|
isPidAlive() {
|
||
|
[ -n "$1" ] && [ "$1" -gt 0 ] || return
|
||
|
[ -d "/proc/$1" ] || kill -0 "$1" 2>/dev/null
|
||
|
}
|
||
|
|
||
|
FAILED=0
|
||
|
PASSED=0
|
||
|
|
||
|
testcase_upsd_no_configs_at_all() {
|
||
|
log_separator
|
||
|
log_info "Test UPSD without configs at all"
|
||
|
upsd -F
|
||
|
if [ "$?" = 0 ]; then
|
||
|
log_error "upsd should fail without configs"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
else
|
||
|
log_info "OK, upsd failed to start in wrong conditions"
|
||
|
PASSED="`expr $PASSED + 1`"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
testcase_upsd_no_configs_driver_file() {
|
||
|
log_separator
|
||
|
log_info "Test UPSD without driver config file"
|
||
|
generatecfg_upsd_trivial
|
||
|
upsd -F
|
||
|
if [ "$?" = 0 ]; then
|
||
|
log_error "upsd should fail without driver config file"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
else
|
||
|
log_info "OK, upsd failed to start in wrong conditions"
|
||
|
PASSED="`expr $PASSED + 1`"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
testcase_upsd_no_configs_in_driver_file() {
|
||
|
log_separator
|
||
|
log_info "Test UPSD without drivers defined in config file"
|
||
|
generatecfg_upsd_trivial
|
||
|
generatecfg_ups_trivial
|
||
|
upsd -F
|
||
|
if [ "$?" = 0 ]; then
|
||
|
log_error "upsd should fail without drivers defined in config file"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
else
|
||
|
log_info "OK, upsd failed to start in wrong conditions"
|
||
|
PASSED="`expr $PASSED + 1`"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
testcase_upsd_allow_no_device() {
|
||
|
log_separator
|
||
|
log_info "Test UPSD allowed to run without driver configs"
|
||
|
generatecfg_upsd_nodev
|
||
|
generatecfg_upsdusers_trivial
|
||
|
generatecfg_ups_trivial
|
||
|
upsd -F &
|
||
|
PID_UPSD="$!"
|
||
|
sleep 2
|
||
|
if isPidAlive "$PID_UPSD"; then
|
||
|
log_info "OK, upsd is running"
|
||
|
PASSED="`expr $PASSED + 1`"
|
||
|
|
||
|
log_separator
|
||
|
log_info "Test that UPSD responds to UPSC"
|
||
|
OUT="`upsc -l localhost:$NUT_PORT`" || die "upsd does not respond: $OUT"
|
||
|
if [ -n "$OUT" ] ; then
|
||
|
log_error "got reply for upsc listing when none was expected: $OUT"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
else
|
||
|
log_info "OK, empty response as expected"
|
||
|
PASSED="`expr $PASSED + 1`"
|
||
|
fi
|
||
|
else
|
||
|
log_error "upsd was expected to be running although no devices are defined"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
fi
|
||
|
kill -15 $PID_UPSD
|
||
|
wait $PID_UPSD
|
||
|
}
|
||
|
|
||
|
testgroup_upsd_invalid_configs() {
|
||
|
testcase_upsd_no_configs_at_all
|
||
|
testcase_upsd_no_configs_driver_file
|
||
|
testcase_upsd_no_configs_in_driver_file
|
||
|
}
|
||
|
|
||
|
testgroup_upsd_questionable_configs() {
|
||
|
testcase_upsd_allow_no_device
|
||
|
}
|
||
|
|
||
|
#########################################################
|
||
|
### Tests in a common sandbox with driver(s) + server ###
|
||
|
#########################################################
|
||
|
|
||
|
SANDBOX_CONFIG_GENERATED=false
|
||
|
sandbox_generate_configs() {
|
||
|
if $SANDBOX_CONFIG_GENERATED ; then return ; fi
|
||
|
|
||
|
log_info "Generating configs for sandbox"
|
||
|
generatecfg_upsd_nodev
|
||
|
generatecfg_upsdusers_trivial
|
||
|
generatecfg_ups_dummy
|
||
|
SANDBOX_CONFIG_GENERATED=true
|
||
|
}
|
||
|
|
||
|
sandbox_forget_configs() {
|
||
|
SANDBOX_CONFIG_GENERATED=false
|
||
|
if [ -z "${DEBUG_SLEEP-}" ] ; then
|
||
|
stop_daemons
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
sandbox_start_upsd() {
|
||
|
if isPidAlive "$PID_UPSD" ; then
|
||
|
return 0
|
||
|
fi
|
||
|
|
||
|
sandbox_generate_configs
|
||
|
|
||
|
log_info "Starting UPSD for sandbox"
|
||
|
upsd -F &
|
||
|
PID_UPSD="$!"
|
||
|
sleep 5
|
||
|
}
|
||
|
|
||
|
sandbox_start_drivers() {
|
||
|
if isPidAlive "$PID_DUMMYUPS" \
|
||
|
&& { [ x"${TOP_SRCDIR}" != x ] && isPidAlive "$PID_DUMMYUPS1" && isPidAlive "$PID_DUMMYUPS2" \
|
||
|
|| [ x"${TOP_SRCDIR}" = x ] ; } \
|
||
|
; then
|
||
|
# All drivers expected for this environment are already running
|
||
|
return 0
|
||
|
fi
|
||
|
|
||
|
sandbox_generate_configs
|
||
|
|
||
|
log_info "Starting dummy-ups driver(s) for sandbox"
|
||
|
#upsdrvctl -F start dummy &
|
||
|
dummy-ups -a dummy -F &
|
||
|
PID_DUMMYUPS="$!"
|
||
|
|
||
|
if [ x"${TOP_SRCDIR}" != x ]; then
|
||
|
dummy-ups -a UPS1 -F &
|
||
|
PID_DUMMYUPS1="$!"
|
||
|
|
||
|
dummy-ups -a UPS2 -F &
|
||
|
PID_DUMMYUPS2="$!"
|
||
|
fi
|
||
|
|
||
|
sleep 5
|
||
|
|
||
|
if shouldDebug ; then
|
||
|
(ps -ef || ps -xawwu) 2>/dev/null | grep -E '(ups|nut|dummy)' || true
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
testcase_sandbox_start_upsd_alone() {
|
||
|
log_separator
|
||
|
log_info "Test starting UPSD but not a driver before it"
|
||
|
sandbox_start_upsd
|
||
|
|
||
|
EXPECTED_UPSLIST='dummy'
|
||
|
if [ x"${TOP_SRCDIR}" != x ]; then
|
||
|
EXPECTED_UPSLIST="$EXPECTED_UPSLIST
|
||
|
UPS1
|
||
|
UPS2"
|
||
|
fi
|
||
|
|
||
|
log_info "Query listing from UPSD by UPSC (driver not running yet)"
|
||
|
OUT="`upsc -l localhost:$NUT_PORT`" || die "upsd does not respond: $OUT"
|
||
|
if [ x"$OUT" != x"$EXPECTED_UPSLIST" ] ; then
|
||
|
log_error "got this reply for upsc listing when '$EXPECTED_UPSLIST' was expected: $OUT"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
else
|
||
|
PASSED="`expr $PASSED + 1`"
|
||
|
fi
|
||
|
|
||
|
log_info "Query driver state from UPSD by UPSC (driver not running yet)"
|
||
|
OUT="`upsc dummy@localhost:$NUT_PORT 2>&1`" && {
|
||
|
log_error "upsc was supposed to answer with error exit code: $OUT"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
}
|
||
|
if ! echo "$OUT" | grep 'Error: Driver not connected' ; then
|
||
|
log_error "got reply for upsc query when 'Error: Driver not connected' was expected: '$OUT'"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
else
|
||
|
PASSED="`expr $PASSED + 1`"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
testcase_sandbox_start_upsd_after_drivers() {
|
||
|
# Historically this is a fallback from testcase_sandbox_start_drivers_after_upsd
|
||
|
kill -15 $PID_UPSD 2>/dev/null
|
||
|
wait $PID_UPSD
|
||
|
upsd -F &
|
||
|
PID_UPSD="$!"
|
||
|
|
||
|
sandbox_start_drivers
|
||
|
sandbox_start_upsd
|
||
|
|
||
|
sleep 5
|
||
|
upsc dummy@localhost:$NUT_PORT || die "upsd does not respond"
|
||
|
}
|
||
|
|
||
|
testcase_sandbox_start_drivers_after_upsd() {
|
||
|
#sandbox_start_upsd
|
||
|
testcase_sandbox_start_upsd_alone
|
||
|
sandbox_start_drivers
|
||
|
|
||
|
log_info "Query driver state from UPSD by UPSC after driver startup"
|
||
|
upsc dummy@localhost:$NUT_PORT || {
|
||
|
# Should not get to this, except on very laggy systems maybe
|
||
|
log_error "Query failed, retrying with UPSD started after drivers"
|
||
|
testcase_sandbox_start_upsd_after_drivers
|
||
|
}
|
||
|
|
||
|
if [ x"${TOP_SRCDIR}" != x ]; then
|
||
|
log_info "Wait for dummy UPSes with larger data sets to initialize"
|
||
|
for U in UPS1 UPS2 ; do
|
||
|
COUNTDOWN=60
|
||
|
while ! upsc $U@localhost:$NUT_PORT ups.status ; do
|
||
|
sleep 1
|
||
|
COUNTDOWN="`expr $COUNTDOWN - 1`"
|
||
|
# Systemic error, e.g. could not create socket file?
|
||
|
[ "$COUNTDOWN" -lt 1 ] && die "Dummy driver did not start or respond in time"
|
||
|
done
|
||
|
done
|
||
|
fi
|
||
|
|
||
|
log_info "Expected drivers are now responding via UPSD"
|
||
|
}
|
||
|
|
||
|
testcase_sandbox_upsc_query_model() {
|
||
|
OUT="`upsc dummy@localhost:$NUT_PORT device.model`" || die "upsd does not respond: $OUT"
|
||
|
if [ x"$OUT" != x"Dummy UPS" ] ; then
|
||
|
log_error "got this reply for upsc query when 'Dummy UPS' was expected: $OUT"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
else
|
||
|
PASSED="`expr $PASSED + 1`"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
testcase_sandbox_upsc_query_bogus() {
|
||
|
log_info "Query driver state from UPSD by UPSC for bogus info"
|
||
|
OUT="`upsc dummy@localhost:$NUT_PORT ups.bogus.value 2>&1`" && {
|
||
|
log_error "upsc was supposed to answer with error exit code: $OUT"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
}
|
||
|
if ! echo "$OUT" | grep 'Error: Variable not supported by UPS' ; then
|
||
|
log_error "got reply for upsc query when 'Error: Variable not supported by UPS' was expected: $OUT"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
else
|
||
|
PASSED="`expr $PASSED + 1`"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
testcase_sandbox_upsc_query_timer() {
|
||
|
log_separator
|
||
|
log_info "Test that dummy-ups TIMER action changes the reported state"
|
||
|
# Driver is set up to flip ups.status every 5 sec, so check every 3
|
||
|
OUT1="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT1" ; sleep 3
|
||
|
OUT2="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT2"
|
||
|
OUT3=""
|
||
|
OUT4=""
|
||
|
if [ x"$OUT1" = x"$OUT2" ]; then
|
||
|
sleep 3
|
||
|
OUT3="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT3"
|
||
|
if [ x"$OUT2" = x"$OUT3" ]; then
|
||
|
sleep 3
|
||
|
OUT4="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT4"
|
||
|
fi
|
||
|
fi
|
||
|
if echo "$OUT1$OUT2$OUT3$OUT4" | grep "OB" && echo "$OUT1$OUT2$OUT3$OUT4" | grep "OL" ; then
|
||
|
log_info "OK, ups.status flips over time"
|
||
|
PASSED="`expr $PASSED + 1`"
|
||
|
else
|
||
|
log_error "ups.status did not flip over time"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
isTestablePython() {
|
||
|
# We optionally make python module (if interpreter is found):
|
||
|
if [ x"${TOP_BUILDDIR}" = x ] \
|
||
|
|| [ ! -x "${TOP_BUILDDIR}/scripts/python/module/test_nutclient.py" ] \
|
||
|
; then
|
||
|
return 1
|
||
|
fi
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
testcase_sandbox_python_without_credentials() {
|
||
|
isTestablePython || return 0
|
||
|
log_separator
|
||
|
log_info "Call Python module test suite: PyNUT (NUT Python bindings) without login credentials"
|
||
|
if ( unset NUT_USER || true
|
||
|
unset NUT_PASS || true
|
||
|
"${TOP_BUILDDIR}/scripts/python/module/test_nutclient.py"
|
||
|
) ; then
|
||
|
log_info "OK, PyNUT did not complain"
|
||
|
PASSED="`expr $PASSED + 1`"
|
||
|
else
|
||
|
log_error "PyNUT complained, check above"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
testcase_sandbox_python_with_credentials() {
|
||
|
isTestablePython || return 0
|
||
|
|
||
|
# That script says it expects data/evolution500.seq (as the UPS1 dummy)
|
||
|
# but the dummy data does not currently let issue the commands and
|
||
|
# setvars tested from python script.
|
||
|
log_separator
|
||
|
log_info "Call Python module test suite: PyNUT (NUT Python bindings) with login credentials"
|
||
|
if (
|
||
|
NUT_USER='admin'
|
||
|
NUT_PASS="${TESTPASS_ADMIN}"
|
||
|
export NUT_USER NUT_PASS
|
||
|
"${TOP_BUILDDIR}/scripts/python/module/test_nutclient.py"
|
||
|
) ; then
|
||
|
log_info "OK, PyNUT did not complain"
|
||
|
PASSED="`expr $PASSED + 1`"
|
||
|
else
|
||
|
log_error "PyNUT complained, check above"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
testcases_sandbox_python() {
|
||
|
isTestablePython || return 0
|
||
|
testcase_sandbox_python_without_credentials
|
||
|
testcase_sandbox_python_with_credentials
|
||
|
}
|
||
|
|
||
|
####################################
|
||
|
|
||
|
isTestableCppNIT() {
|
||
|
# We optionally make and here can run C++ client tests:
|
||
|
if [ x"${TOP_BUILDDIR}" = x ] \
|
||
|
|| [ ! -x "${TOP_BUILDDIR}/tests/cppnit" ] \
|
||
|
; then
|
||
|
return 1
|
||
|
fi
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
testcase_sandbox_cppnit_without_creds() {
|
||
|
isTestableCppNIT || return 0
|
||
|
|
||
|
log_separator
|
||
|
log_info "Call libnutclient test suite: cppnit without login credentials"
|
||
|
if ( unset NUT_USER || true
|
||
|
unset NUT_PASS || true
|
||
|
"${TOP_BUILDDIR}/tests/cppnit"
|
||
|
) ; then
|
||
|
log_info "OK, cppnit did not complain"
|
||
|
PASSED="`expr $PASSED + 1`"
|
||
|
else
|
||
|
log_error "cppnit complained, check above"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
testcase_sandbox_cppnit_simple_admin() {
|
||
|
isTestableCppNIT || return 0
|
||
|
|
||
|
log_separator
|
||
|
log_info "Call libnutclient test suite: cppnit with login credentials: simple admin"
|
||
|
if (
|
||
|
NUT_USER='admin'
|
||
|
NUT_PASS="${TESTPASS_ADMIN}"
|
||
|
if [ x"${TOP_SRCDIR}" != x ]; then
|
||
|
# Avoid dummies with TIMER flip-flops
|
||
|
NUT_SETVAR_DEVICE='UPS2'
|
||
|
else
|
||
|
# Risks failure when lauching sub-test at the wrong second
|
||
|
NUT_SETVAR_DEVICE='dummy'
|
||
|
fi
|
||
|
unset NUT_PRIMARY_DEVICE
|
||
|
export NUT_USER NUT_PASS NUT_SETVAR_DEVICE
|
||
|
"${TOP_BUILDDIR}/tests/cppnit"
|
||
|
) ; then
|
||
|
log_info "OK, cppnit did not complain"
|
||
|
PASSED="`expr $PASSED + 1`"
|
||
|
else
|
||
|
log_error "cppnit complained, check above"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
testcase_sandbox_cppnit_upsmon_primary() {
|
||
|
isTestableCppNIT || return 0
|
||
|
|
||
|
log_separator
|
||
|
log_info "Call libnutclient test suite: cppnit with login credentials: upsmon-primary"
|
||
|
if (
|
||
|
NUT_USER='dummy-admin'
|
||
|
NUT_PASS="${TESTPASS_UPSMON_PRIMARY}"
|
||
|
NUT_PRIMARY_DEVICE='dummy'
|
||
|
unset NUT_SETVAR_DEVICE
|
||
|
export NUT_USER NUT_PASS NUT_PRIMARY_DEVICE
|
||
|
"${TOP_BUILDDIR}/tests/cppnit"
|
||
|
) ; then
|
||
|
log_info "OK, cppnit did not complain"
|
||
|
PASSED="`expr $PASSED + 1`"
|
||
|
else
|
||
|
log_error "cppnit complained, check above"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
testcase_sandbox_cppnit_upsmon_master() {
|
||
|
isTestableCppNIT || return 0
|
||
|
|
||
|
log_separator
|
||
|
log_info "Call libnutclient test suite: cppnit with login credentials: upsmon-master"
|
||
|
if (
|
||
|
NUT_USER='dummy-admin-m'
|
||
|
NUT_PASS="${TESTPASS_UPSMON_PRIMARY}"
|
||
|
NUT_PRIMARY_DEVICE='dummy'
|
||
|
unset NUT_SETVAR_DEVICE
|
||
|
export NUT_USER NUT_PASS NUT_PRIMARY_DEVICE
|
||
|
"${TOP_BUILDDIR}/tests/cppnit"
|
||
|
) ; then
|
||
|
log_info "OK, cppnit did not complain"
|
||
|
PASSED="`expr $PASSED + 1`"
|
||
|
else
|
||
|
log_error "cppnit complained, check above"
|
||
|
FAILED="`expr $FAILED + 1`"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
testcases_sandbox_cppnit() {
|
||
|
isTestableCppNIT || return 0
|
||
|
testcase_sandbox_cppnit_without_creds
|
||
|
testcase_sandbox_cppnit_upsmon_primary
|
||
|
testcase_sandbox_cppnit_upsmon_master
|
||
|
testcase_sandbox_cppnit_simple_admin
|
||
|
}
|
||
|
|
||
|
# TODO: Some upsmon tests?
|
||
|
|
||
|
testgroup_sandbox() {
|
||
|
testcase_sandbox_start_drivers_after_upsd
|
||
|
testcase_sandbox_upsc_query_model
|
||
|
testcase_sandbox_upsc_query_bogus
|
||
|
testcase_sandbox_upsc_query_timer
|
||
|
testcases_sandbox_python
|
||
|
testcases_sandbox_cppnit
|
||
|
|
||
|
sandbox_forget_configs
|
||
|
}
|
||
|
|
||
|
testgroup_sandbox_python() {
|
||
|
# Arrange for quick test iterations
|
||
|
testcase_sandbox_start_drivers_after_upsd
|
||
|
testcases_sandbox_python
|
||
|
sandbox_forget_configs
|
||
|
}
|
||
|
|
||
|
testgroup_sandbox_cppnit() {
|
||
|
# Arrange for quick test iterations
|
||
|
testcase_sandbox_start_drivers_after_upsd
|
||
|
testcases_sandbox_cppnit
|
||
|
sandbox_forget_configs
|
||
|
}
|
||
|
|
||
|
testgroup_sandbox_cppnit_simple_admin() {
|
||
|
# Arrange for quick test iterations
|
||
|
testcase_sandbox_start_drivers_after_upsd
|
||
|
testcase_sandbox_cppnit_simple_admin
|
||
|
sandbox_forget_configs
|
||
|
}
|
||
|
|
||
|
################################################################
|
||
|
|
||
|
case "${NIT_CASE}" in
|
||
|
cppnit) testgroup_sandbox_cppnit ;;
|
||
|
python) testgroup_sandbox_python ;;
|
||
|
testcase_*|testgroup_*|testcases_*|testgroups_*)
|
||
|
"${NIT_CASE}" ;;
|
||
|
"") # Default test groups:
|
||
|
testgroup_upsd_invalid_configs
|
||
|
testgroup_upsd_questionable_configs
|
||
|
testgroup_sandbox
|
||
|
;;
|
||
|
*) die "Unsupported NIT_CASE='$NIT_CASE' was requested" ;;
|
||
|
esac
|
||
|
|
||
|
log_separator
|
||
|
log_info "OVERALL: PASSED=$PASSED FAILED=$FAILED"
|
||
|
|
||
|
# Allow to leave the sandbox daemons running for a while,
|
||
|
# to experiment with them interactively:
|
||
|
if [ -n "${DEBUG_SLEEP-}" ] ; then
|
||
|
log_separator
|
||
|
log_info "Sleeping now as asked, so you can play with the driver and server running; hint: export NUT_PORT=$NUT_PORT"
|
||
|
log_separator
|
||
|
if [ "${DEBUG_SLEEP-}" -gt 0 ] ; then
|
||
|
sleep "${DEBUG_SLEEP}"
|
||
|
else
|
||
|
sleep 60
|
||
|
fi
|
||
|
log_info "Sleep finished"
|
||
|
log_separator
|
||
|
fi
|
||
|
|
||
|
if [ "$PASSED" = 0 ] || [ "$FAILED" != 0 ] ; then
|
||
|
die "Some test scenarios failed!"
|
||
|
else
|
||
|
log_info "SUCCESS"
|
||
|
fi
|