1*cf5a6c84SAndroid Build Coastguard Worker# Simple test harness infrastructure 2*cf5a6c84SAndroid Build Coastguard Worker# 3*cf5a6c84SAndroid Build Coastguard Worker# Copyright 2005 by Rob Landley 4*cf5a6c84SAndroid Build Coastguard Worker 5*cf5a6c84SAndroid Build Coastguard Worker# This file defines three main functions: "testing", "testcmd", and "txpect". 6*cf5a6c84SAndroid Build Coastguard Worker 7*cf5a6c84SAndroid Build Coastguard Worker# The following environment variables enable optional behavior in "testing": 8*cf5a6c84SAndroid Build Coastguard Worker# DEBUG - Show every command run by test script. 9*cf5a6c84SAndroid Build Coastguard Worker# VERBOSE - "all" continue after failed test 10*cf5a6c84SAndroid Build Coastguard Worker# "fail" show diff and stop at first failed test 11*cf5a6c84SAndroid Build Coastguard Worker# "nopass" don't show successful tests 12*cf5a6c84SAndroid Build Coastguard Worker# "quiet" don't show diff -u for failures 13*cf5a6c84SAndroid Build Coastguard Worker# "spam" show passing test command lines 14*cf5a6c84SAndroid Build Coastguard Worker# 15*cf5a6c84SAndroid Build Coastguard Worker# The "testcmd" function takes five arguments: 16*cf5a6c84SAndroid Build Coastguard Worker# $1) Description to display when running command 17*cf5a6c84SAndroid Build Coastguard Worker# $2) Command line arguments to command 18*cf5a6c84SAndroid Build Coastguard Worker# $3) Expected result (on stdout) 19*cf5a6c84SAndroid Build Coastguard Worker# $4) Data written to file "input" 20*cf5a6c84SAndroid Build Coastguard Worker# $5) Data written to stdin 21*cf5a6c84SAndroid Build Coastguard Worker# 22*cf5a6c84SAndroid Build Coastguard Worker# The "testing" function is like testcmd but takes a complete command line 23*cf5a6c84SAndroid Build Coastguard Worker# (I.E. you have to include the command name.) The variable $C is an absolute 24*cf5a6c84SAndroid Build Coastguard Worker# path to the command being tested, which can bypass shell builtins. 25*cf5a6c84SAndroid Build Coastguard Worker# 26*cf5a6c84SAndroid Build Coastguard Worker# The exit value of testcmd is the exit value of the command it ran. 27*cf5a6c84SAndroid Build Coastguard Worker# 28*cf5a6c84SAndroid Build Coastguard Worker# The environment variable "FAILCOUNT" contains a cumulative total of the 29*cf5a6c84SAndroid Build Coastguard Worker# number of failed tests. 30*cf5a6c84SAndroid Build Coastguard Worker# 31*cf5a6c84SAndroid Build Coastguard Worker# The environment variable "SKIP" says how many upcoming tests to skip, 32*cf5a6c84SAndroid Build Coastguard Worker# defaulting to 0 and counting down when set to a higher number. 33*cf5a6c84SAndroid Build Coastguard Worker# 34*cf5a6c84SAndroid Build Coastguard Worker# Function "optional" enables/disables tests based on configuration options. 35*cf5a6c84SAndroid Build Coastguard Worker 36*cf5a6c84SAndroid Build Coastguard Workerexport FAILCOUNT=0 SKIP=0 37*cf5a6c84SAndroid Build Coastguard Worker: ${SHOWPASS:=PASS} ${SHOWFAIL:=FAIL} ${SHOWSKIP:=SKIP} 38*cf5a6c84SAndroid Build Coastguard Workerif tty -s <&1 39*cf5a6c84SAndroid Build Coastguard Workerthen 40*cf5a6c84SAndroid Build Coastguard Worker SHOWPASS="$(echo -e "\033[1;32m${SHOWPASS}\033[0m")" 41*cf5a6c84SAndroid Build Coastguard Worker SHOWFAIL="$(echo -e "\033[1;31m${SHOWFAIL}\033[0m")" 42*cf5a6c84SAndroid Build Coastguard Worker SHOWSKIP="$(echo -e "\033[1;33m${SHOWSKIP}\033[0m")" 43*cf5a6c84SAndroid Build Coastguard Workerfi 44*cf5a6c84SAndroid Build Coastguard Worker 45*cf5a6c84SAndroid Build Coastguard Worker# Helper functions 46*cf5a6c84SAndroid Build Coastguard Worker 47*cf5a6c84SAndroid Build Coastguard Worker# Check if VERBOSE= contains a given string. (This allows combining.) 48*cf5a6c84SAndroid Build Coastguard Workerverbose_has() 49*cf5a6c84SAndroid Build Coastguard Worker{ 50*cf5a6c84SAndroid Build Coastguard Worker [ "${VERBOSE/$1/}" != "$VERBOSE" ] 51*cf5a6c84SAndroid Build Coastguard Worker} 52*cf5a6c84SAndroid Build Coastguard Worker 53*cf5a6c84SAndroid Build Coastguard Workerwrong_args() 54*cf5a6c84SAndroid Build Coastguard Worker{ 55*cf5a6c84SAndroid Build Coastguard Worker if [ $# -ne 5 ] 56*cf5a6c84SAndroid Build Coastguard Worker then 57*cf5a6c84SAndroid Build Coastguard Worker printf "%s\n" "Test $NAME has the wrong number of arguments ($# $*)" >&2 58*cf5a6c84SAndroid Build Coastguard Worker exit 59*cf5a6c84SAndroid Build Coastguard Worker fi 60*cf5a6c84SAndroid Build Coastguard Worker} 61*cf5a6c84SAndroid Build Coastguard Worker 62*cf5a6c84SAndroid Build Coastguard Worker# Announce success 63*cf5a6c84SAndroid Build Coastguard Workerdo_pass() 64*cf5a6c84SAndroid Build Coastguard Worker{ 65*cf5a6c84SAndroid Build Coastguard Worker verbose_has nopass || printf "%s\n" "$SHOWPASS: $NAME" 66*cf5a6c84SAndroid Build Coastguard Worker} 67*cf5a6c84SAndroid Build Coastguard Worker 68*cf5a6c84SAndroid Build Coastguard Worker# Announce failure and handle fallout for txpect 69*cf5a6c84SAndroid Build Coastguard Workerdo_fail() 70*cf5a6c84SAndroid Build Coastguard Worker{ 71*cf5a6c84SAndroid Build Coastguard Worker FAILCOUNT=$(($FAILCOUNT+1)) 72*cf5a6c84SAndroid Build Coastguard Worker printf "%s\n" "$SHOWFAIL: $NAME" 73*cf5a6c84SAndroid Build Coastguard Worker if [ ! -z "$CASE" ] 74*cf5a6c84SAndroid Build Coastguard Worker then 75*cf5a6c84SAndroid Build Coastguard Worker echo "Expected '$CASE'" 76*cf5a6c84SAndroid Build Coastguard Worker echo "Got '$A'" 77*cf5a6c84SAndroid Build Coastguard Worker fi 78*cf5a6c84SAndroid Build Coastguard Worker ! verbose_has all && exit 1 79*cf5a6c84SAndroid Build Coastguard Worker} 80*cf5a6c84SAndroid Build Coastguard Worker 81*cf5a6c84SAndroid Build Coastguard Worker# Functions test files call directly 82*cf5a6c84SAndroid Build Coastguard Worker 83*cf5a6c84SAndroid Build Coastguard Worker# Set SKIP high if option not enabled in $OPTIONFLAGS (unless OPTIONFLAGS blank) 84*cf5a6c84SAndroid Build Coastguard Workeroptional() 85*cf5a6c84SAndroid Build Coastguard Worker{ 86*cf5a6c84SAndroid Build Coastguard Worker [ -n "$OPTIONFLAGS" ] && [ "$OPTIONFLAGS" == "${OPTIONFLAGS/:$1:/}" ] && 87*cf5a6c84SAndroid Build Coastguard Worker SKIP=99999 || SKIP=0 88*cf5a6c84SAndroid Build Coastguard Worker} 89*cf5a6c84SAndroid Build Coastguard Worker 90*cf5a6c84SAndroid Build Coastguard Worker# Evalute command line and skip next test when false 91*cf5a6c84SAndroid Build Coastguard Workerskipnot() 92*cf5a6c84SAndroid Build Coastguard Worker{ 93*cf5a6c84SAndroid Build Coastguard Worker if verbose_has quiet 94*cf5a6c84SAndroid Build Coastguard Worker then 95*cf5a6c84SAndroid Build Coastguard Worker eval "$@" >/dev/null 2>&1 96*cf5a6c84SAndroid Build Coastguard Worker else 97*cf5a6c84SAndroid Build Coastguard Worker eval "$@" 98*cf5a6c84SAndroid Build Coastguard Worker fi 99*cf5a6c84SAndroid Build Coastguard Worker [ $? -eq 0 ] || { ((++SKIP)); return 1; } 100*cf5a6c84SAndroid Build Coastguard Worker} 101*cf5a6c84SAndroid Build Coastguard Worker 102*cf5a6c84SAndroid Build Coastguard Worker# Skip this test (rest of command line) when not running toybox. 103*cf5a6c84SAndroid Build Coastguard Workertoyonly() 104*cf5a6c84SAndroid Build Coastguard Worker{ 105*cf5a6c84SAndroid Build Coastguard Worker IS_TOYBOX="$("$C" --version 2>/dev/null)" 106*cf5a6c84SAndroid Build Coastguard Worker # Ideally we'd just check for "toybox", but toybox sed lies to make autoconf 107*cf5a6c84SAndroid Build Coastguard Worker # happy, so we have at least two things to check for. 108*cf5a6c84SAndroid Build Coastguard Worker case "$IS_TOYBOX" in 109*cf5a6c84SAndroid Build Coastguard Worker toybox*) ;; 110*cf5a6c84SAndroid Build Coastguard Worker This\ is\ not\ GNU*) ;; 111*cf5a6c84SAndroid Build Coastguard Worker *) [ $SKIP -eq 0 ] && ((++SKIP)) ;; 112*cf5a6c84SAndroid Build Coastguard Worker esac 113*cf5a6c84SAndroid Build Coastguard Worker 114*cf5a6c84SAndroid Build Coastguard Worker "$@" 115*cf5a6c84SAndroid Build Coastguard Worker} 116*cf5a6c84SAndroid Build Coastguard Worker 117*cf5a6c84SAndroid Build Coastguard Worker# Takes five arguments: "name" "command" "result" "infile" "stdin" 118*cf5a6c84SAndroid Build Coastguard Workertesting() 119*cf5a6c84SAndroid Build Coastguard Worker{ 120*cf5a6c84SAndroid Build Coastguard Worker wrong_args "$@" 121*cf5a6c84SAndroid Build Coastguard Worker 122*cf5a6c84SAndroid Build Coastguard Worker [ -z "$1" ] && NAME="$2" || NAME="$1" 123*cf5a6c84SAndroid Build Coastguard Worker [ "${NAME#$CMDNAME }" == "$NAME" ] && NAME="$CMDNAME $1" 124*cf5a6c84SAndroid Build Coastguard Worker 125*cf5a6c84SAndroid Build Coastguard Worker [ -n "$DEBUG" ] && set -x 126*cf5a6c84SAndroid Build Coastguard Worker 127*cf5a6c84SAndroid Build Coastguard Worker if [ "$SKIP" -gt 0 ] 128*cf5a6c84SAndroid Build Coastguard Worker then 129*cf5a6c84SAndroid Build Coastguard Worker verbose_has quiet || printf "%s\n" "$SHOWSKIP: $NAME" 130*cf5a6c84SAndroid Build Coastguard Worker ((--SKIP)) 131*cf5a6c84SAndroid Build Coastguard Worker 132*cf5a6c84SAndroid Build Coastguard Worker return 0 133*cf5a6c84SAndroid Build Coastguard Worker fi 134*cf5a6c84SAndroid Build Coastguard Worker 135*cf5a6c84SAndroid Build Coastguard Worker echo -ne "$3" > "$TESTDIR"/expected 136*cf5a6c84SAndroid Build Coastguard Worker [ ! -z "$4" ] && echo -ne "$4" > input || rm -f input 137*cf5a6c84SAndroid Build Coastguard Worker echo -ne "$5" | ${EVAL:-eval --} "$2" > "$TESTDIR"/actual 138*cf5a6c84SAndroid Build Coastguard Worker RETVAL=$? 139*cf5a6c84SAndroid Build Coastguard Worker 140*cf5a6c84SAndroid Build Coastguard Worker # Catch segfaults 141*cf5a6c84SAndroid Build Coastguard Worker [ $RETVAL -gt 128 ] && 142*cf5a6c84SAndroid Build Coastguard Worker echo "exited with signal (or returned $RETVAL)" >> actual 143*cf5a6c84SAndroid Build Coastguard Worker DIFF="$(cd "$TESTDIR"; diff -au${NOSPACE:+w} expected actual 2>&1)" 144*cf5a6c84SAndroid Build Coastguard Worker [ -z "$DIFF" ] && do_pass || VERBOSE=all do_fail 145*cf5a6c84SAndroid Build Coastguard Worker if ! verbose_has quiet && { [ -n "$DIFF" ] || verbose_has spam; } 146*cf5a6c84SAndroid Build Coastguard Worker then 147*cf5a6c84SAndroid Build Coastguard Worker [ ! -z "$4" ] && printf "%s\n" "echo -ne \"$4\" > input" 148*cf5a6c84SAndroid Build Coastguard Worker printf "%s\n" "echo -ne '$5' |$EVAL $2" 149*cf5a6c84SAndroid Build Coastguard Worker [ -n "$DIFF" ] && printf "%s\n" "$DIFF" 150*cf5a6c84SAndroid Build Coastguard Worker fi 151*cf5a6c84SAndroid Build Coastguard Worker 152*cf5a6c84SAndroid Build Coastguard Worker [ -n "$DIFF" ] && ! verbose_has all && exit 1 153*cf5a6c84SAndroid Build Coastguard Worker rm -f input ../expected ../actual 154*cf5a6c84SAndroid Build Coastguard Worker 155*cf5a6c84SAndroid Build Coastguard Worker [ -n "$DEBUG" ] && set +x 156*cf5a6c84SAndroid Build Coastguard Worker 157*cf5a6c84SAndroid Build Coastguard Worker return 0 158*cf5a6c84SAndroid Build Coastguard Worker} 159*cf5a6c84SAndroid Build Coastguard Worker 160*cf5a6c84SAndroid Build Coastguard Worker# Wrapper for "testing", adds command name being tested to start of command line 161*cf5a6c84SAndroid Build Coastguard Workertestcmd() 162*cf5a6c84SAndroid Build Coastguard Worker{ 163*cf5a6c84SAndroid Build Coastguard Worker wrong_args "$@" 164*cf5a6c84SAndroid Build Coastguard Worker 165*cf5a6c84SAndroid Build Coastguard Worker testing "${1:-$CMDNAME $2}" "\"$C\" $2" "$3" "$4" "$5" 166*cf5a6c84SAndroid Build Coastguard Worker} 167*cf5a6c84SAndroid Build Coastguard Worker 168*cf5a6c84SAndroid Build Coastguard Workerutf8locale() 169*cf5a6c84SAndroid Build Coastguard Worker{ 170*cf5a6c84SAndroid Build Coastguard Worker local i 171*cf5a6c84SAndroid Build Coastguard Worker 172*cf5a6c84SAndroid Build Coastguard Worker for i in $LC_ALL C.UTF-8 en_US.UTF-8 173*cf5a6c84SAndroid Build Coastguard Worker do 174*cf5a6c84SAndroid Build Coastguard Worker [ "$(LC_ALL=$i locale charmap 2>/dev/null)" == UTF-8 ] && LC_ALL=$i && break 175*cf5a6c84SAndroid Build Coastguard Worker done 176*cf5a6c84SAndroid Build Coastguard Worker} 177*cf5a6c84SAndroid Build Coastguard Worker 178*cf5a6c84SAndroid Build Coastguard Worker# Simple implementation of "expect" written in shell. 179*cf5a6c84SAndroid Build Coastguard Worker 180*cf5a6c84SAndroid Build Coastguard Worker# txpect NAME COMMAND [I/O/E/X/R[OE]string]... 181*cf5a6c84SAndroid Build Coastguard Worker# Run COMMAND and interact with it: 182*cf5a6c84SAndroid Build Coastguard Worker# I send string to input 183*cf5a6c84SAndroid Build Coastguard Worker# OE read exactly this string from stdout or stderr (bare = read+discard line) 184*cf5a6c84SAndroid Build Coastguard Worker# note: non-bare does not read \n unless you include it with O$'blah\n' 185*cf5a6c84SAndroid Build Coastguard Worker# R prefix means O or E is regex match (read line, must contain substring) 186*cf5a6c84SAndroid Build Coastguard Worker# X close stdin/stdout/stderr and match return code (blank means nonzero) 187*cf5a6c84SAndroid Build Coastguard Workertxpect() 188*cf5a6c84SAndroid Build Coastguard Worker{ 189*cf5a6c84SAndroid Build Coastguard Worker local NAME CASE VERBOSITY IN OUT ERR LEN PID A B X O 190*cf5a6c84SAndroid Build Coastguard Worker 191*cf5a6c84SAndroid Build Coastguard Worker # Run command with redirection through fifos 192*cf5a6c84SAndroid Build Coastguard Worker NAME="$CMDNAME $1" 193*cf5a6c84SAndroid Build Coastguard Worker CASE= 194*cf5a6c84SAndroid Build Coastguard Worker VERBOSITY= 195*cf5a6c84SAndroid Build Coastguard Worker 196*cf5a6c84SAndroid Build Coastguard Worker if [ $# -lt 2 ] || ! mkfifo in-$$ out-$$ err-$$ 197*cf5a6c84SAndroid Build Coastguard Worker then 198*cf5a6c84SAndroid Build Coastguard Worker do_fail 199*cf5a6c84SAndroid Build Coastguard Worker return 200*cf5a6c84SAndroid Build Coastguard Worker fi 201*cf5a6c84SAndroid Build Coastguard Worker eval "$2" <in-$$ >out-$$ 2>err-$$ & 202*cf5a6c84SAndroid Build Coastguard Worker PID=$! 203*cf5a6c84SAndroid Build Coastguard Worker shift 2 204*cf5a6c84SAndroid Build Coastguard Worker : {IN}>in-$$ {OUT}<out-$$ {ERR}<err-$$ && rm in-$$ out-$$ err-$$ 205*cf5a6c84SAndroid Build Coastguard Worker 206*cf5a6c84SAndroid Build Coastguard Worker [ $? -ne 0 ] && { do_fail;return;} 207*cf5a6c84SAndroid Build Coastguard Worker 208*cf5a6c84SAndroid Build Coastguard Worker # Loop through challenge/response pairs, with 2 second timeout 209*cf5a6c84SAndroid Build Coastguard Worker while [ $# -gt 0 -a -n "$PID" ] 210*cf5a6c84SAndroid Build Coastguard Worker do 211*cf5a6c84SAndroid Build Coastguard Worker VERBOSITY="$VERBOSITY"$'\n'"$1" LEN=$((${#1}-1)) CASE="$1" A= B= 212*cf5a6c84SAndroid Build Coastguard Worker 213*cf5a6c84SAndroid Build Coastguard Worker verbose_has spam && echo "txpect $CASE" 214*cf5a6c84SAndroid Build Coastguard Worker case ${1::1} in 215*cf5a6c84SAndroid Build Coastguard Worker 216*cf5a6c84SAndroid Build Coastguard Worker # send input to child 217*cf5a6c84SAndroid Build Coastguard Worker I) printf %s "${1:1}" >&$IN || { do_fail;break;} ;; 218*cf5a6c84SAndroid Build Coastguard Worker 219*cf5a6c84SAndroid Build Coastguard Worker R) LEN=0; B=1; ;& 220*cf5a6c84SAndroid Build Coastguard Worker # check output from child 221*cf5a6c84SAndroid Build Coastguard Worker [OE]) 222*cf5a6c84SAndroid Build Coastguard Worker [ $LEN == 0 ] && LARG="" || LARG="-rN $LEN" 223*cf5a6c84SAndroid Build Coastguard Worker O=$OUT A= 224*cf5a6c84SAndroid Build Coastguard Worker [ "${1:$B:1}" == 'E' ] && O=$ERR 225*cf5a6c84SAndroid Build Coastguard Worker read -t2 $LARG A <&$O 226*cf5a6c84SAndroid Build Coastguard Worker X=$? 227*cf5a6c84SAndroid Build Coastguard Worker verbose_has spam && echo "txgot $X '$A'" 228*cf5a6c84SAndroid Build Coastguard Worker VERBOSITY="$VERBOSITY"$'\n'"$A" 229*cf5a6c84SAndroid Build Coastguard Worker if [ $LEN -eq 0 ] 230*cf5a6c84SAndroid Build Coastguard Worker then 231*cf5a6c84SAndroid Build Coastguard Worker [ -z "$A" -o "$X" -ne 0 ] && { do_fail;break;} 232*cf5a6c84SAndroid Build Coastguard Worker else 233*cf5a6c84SAndroid Build Coastguard Worker if [ ${1::1} == 'R' ] && grep -q "${1:2}" <<< "$A"; then true 234*cf5a6c84SAndroid Build Coastguard Worker elif [ ${1::1} != 'R' ] && [ "$A" == "${1:1}" ]; then true 235*cf5a6c84SAndroid Build Coastguard Worker else 236*cf5a6c84SAndroid Build Coastguard Worker # Append the rest of the output if there is any. 237*cf5a6c84SAndroid Build Coastguard Worker read -t.1 B <&$O 238*cf5a6c84SAndroid Build Coastguard Worker A="$A$B" 239*cf5a6c84SAndroid Build Coastguard Worker read -t.1 -rN 9999 B<&$ERR 240*cf5a6c84SAndroid Build Coastguard Worker do_fail 241*cf5a6c84SAndroid Build Coastguard Worker break 242*cf5a6c84SAndroid Build Coastguard Worker fi 243*cf5a6c84SAndroid Build Coastguard Worker fi 244*cf5a6c84SAndroid Build Coastguard Worker ;; 245*cf5a6c84SAndroid Build Coastguard Worker 246*cf5a6c84SAndroid Build Coastguard Worker # close I/O and wait for exit 247*cf5a6c84SAndroid Build Coastguard Worker X) 248*cf5a6c84SAndroid Build Coastguard Worker exec {IN}<&- 249*cf5a6c84SAndroid Build Coastguard Worker wait $PID 250*cf5a6c84SAndroid Build Coastguard Worker A=$? 251*cf5a6c84SAndroid Build Coastguard Worker exec {OUT}<&- {ERR}<&- 252*cf5a6c84SAndroid Build Coastguard Worker if [ "$LEN" -eq 0 ] 253*cf5a6c84SAndroid Build Coastguard Worker then 254*cf5a6c84SAndroid Build Coastguard Worker [ $A -eq 0 ] && { do_fail;break;} # any error 255*cf5a6c84SAndroid Build Coastguard Worker else 256*cf5a6c84SAndroid Build Coastguard Worker [ $A != "${1:1}" ] && { do_fail;break;} # specific value 257*cf5a6c84SAndroid Build Coastguard Worker fi 258*cf5a6c84SAndroid Build Coastguard Worker do_pass 259*cf5a6c84SAndroid Build Coastguard Worker return 260*cf5a6c84SAndroid Build Coastguard Worker ;; 261*cf5a6c84SAndroid Build Coastguard Worker *) do_fail; break ;; 262*cf5a6c84SAndroid Build Coastguard Worker esac 263*cf5a6c84SAndroid Build Coastguard Worker shift 264*cf5a6c84SAndroid Build Coastguard Worker done 265*cf5a6c84SAndroid Build Coastguard Worker # In case we already closed it 266*cf5a6c84SAndroid Build Coastguard Worker exec {IN}<&- {OUT}<&- {ERR}<&- 267*cf5a6c84SAndroid Build Coastguard Worker 268*cf5a6c84SAndroid Build Coastguard Worker if [ $# -eq 0 ] 269*cf5a6c84SAndroid Build Coastguard Worker then 270*cf5a6c84SAndroid Build Coastguard Worker do_pass 271*cf5a6c84SAndroid Build Coastguard Worker else 272*cf5a6c84SAndroid Build Coastguard Worker ! verbose_has quiet && echo "$VERBOSITY" >&2 273*cf5a6c84SAndroid Build Coastguard Worker do_fail 274*cf5a6c84SAndroid Build Coastguard Worker fi 275*cf5a6c84SAndroid Build Coastguard Worker} 276